diff --git a/content/base/public/nsContentUtils.h b/content/base/public/nsContentUtils.h index 9dcf14c2334..abffc55e735 100644 --- a/content/base/public/nsContentUtils.h +++ b/content/base/public/nsContentUtils.h @@ -1830,6 +1830,11 @@ public: */ static bool IsRequestFullScreenAllowed(); + /** + * Returns true if the idle observers API is enabled. + */ + static bool IsIdleObserverAPIEnabled() { return sIsIdleObserverAPIEnabled; } + /** * Returns true if the doc tree branch which contains aDoc contains any * plugins which we don't control event dispatch for, i.e. do any plugins @@ -2013,6 +2018,19 @@ public: static void SplitMimeType(const nsAString& aValue, nsString& aType, nsString& aParams); + /** + * Function checks if the user is idle. + * + * @param aRequestedIdleTimeInMS The idle observer's requested idle time. + * @param aUserIsIdle boolean indicating if the user + * is currently idle or not. * + * @return NS_OK NS_OK returned if the requested idle service and + * the current idle time were successfully obtained. + * NS_ERROR_FAILURE returned if the the requested + * idle service or the current idle were not obtained. + */ + static nsresult IsUserIdle(PRUint32 aRequestedIdleTimeInMS, bool* aUserIsIdle); + /** * Takes a window and a string to check prefs against. Assumes that * the window is an app window, and that the pref is a comma @@ -2139,6 +2157,7 @@ private: static bool sIsFullScreenApiEnabled; static bool sTrustedFullScreenOnly; static PRUint32 sHandlingInputTimeout; + static bool sIsIdleObserverAPIEnabled; static nsHtml5StringParser* sHTMLFragmentParser; static nsIParser* sXMLFragmentParser; diff --git a/content/base/src/nsContentUtils.cpp b/content/base/src/nsContentUtils.cpp index 996c9bdde73..36f754bb17c 100644 --- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -29,6 +29,7 @@ #include "nsIDocument.h" #include "nsINodeInfo.h" #include "nsReadableUtils.h" +#include "nsIIdleService.h" #include "nsIDOMDocument.h" #include "nsIDOMNodeList.h" #include "nsIDOMNode.h" @@ -243,6 +244,7 @@ nsString* nsContentUtils::sModifierSeparator = nsnull; bool nsContentUtils::sInitialized = false; bool nsContentUtils::sIsFullScreenApiEnabled = false; bool nsContentUtils::sTrustedFullScreenOnly = true; +bool nsContentUtils::sIsIdleObserverAPIEnabled = false; PRUint32 nsContentUtils::sHandlingInputTimeout = 1000; @@ -418,6 +420,8 @@ nsContentUtils::Init() Preferences::AddBoolVarCache(&sTrustedFullScreenOnly, "full-screen-api.allow-trusted-requests-only"); + sIsIdleObserverAPIEnabled = Preferences::GetBool("dom.idle-observers-api.enabled", true); + Preferences::AddUintVarCache(&sHandlingInputTimeout, "dom.event.handling-user-input-time-limit", 1000); @@ -869,6 +873,22 @@ nsContentUtils::SplitMimeType(const nsAString& aValue, nsString& aType, aType.StripWhitespace(); } +nsresult +nsContentUtils::IsUserIdle(PRUint32 aRequestedIdleTimeInMS, bool* aUserIsIdle) +{ + nsresult rv; + nsCOMPtr idleService = + do_GetService("@mozilla.org/widget/idleservice;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + PRUint32 idleTimeInMS; + rv = idleService->GetIdleTime(&idleTimeInMS); + NS_ENSURE_SUCCESS(rv, rv); + + *aUserIsIdle = idleTimeInMS >= aRequestedIdleTimeInMS; + return NS_OK; +} + /** * Access a cached parser service. Don't addref. We need only one * reference to it and this class has that one. diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp index b500b74a985..6ab8c4a3b85 100644 --- a/dom/base/Navigator.cpp +++ b/dom/base/Navigator.cpp @@ -37,6 +37,7 @@ #include "mozilla/ClearOnShutdown.h" #include "Connection.h" #include "MobileConnection.h" +#include "nsIIdleObserver.h" #ifdef MOZ_MEDIA_NAVIGATOR #include "MediaManager.h" @@ -650,6 +651,43 @@ GetVibrationDurationFromJsval(const jsval& aJSVal, JSContext* cx, } // anonymous namespace +NS_IMETHODIMP +Navigator::AddIdleObserver(nsIIdleObserver* aIdleObserver) +{ + if (!nsContentUtils::IsIdleObserverAPIEnabled()) { + NS_WARNING("The IdleObserver API has been disabled."); + return NS_OK; + } + + NS_ENSURE_ARG_POINTER(aIdleObserver); + + nsCOMPtr win = do_QueryReferent(mWindow); + NS_ENSURE_TRUE(win, NS_ERROR_UNEXPECTED); + if (NS_FAILED(win->RegisterIdleObserver(aIdleObserver))) { + NS_WARNING("Failed to add idle observer."); + } + + return NS_OK; +} + +NS_IMETHODIMP +Navigator::RemoveIdleObserver(nsIIdleObserver* aIdleObserver) +{ + if (!nsContentUtils::IsIdleObserverAPIEnabled()) { + NS_WARNING("The IdleObserver API has been disabled"); + return NS_OK; + } + + NS_ENSURE_ARG_POINTER(aIdleObserver); + + nsCOMPtr win = do_QueryReferent(mWindow); + NS_ENSURE_TRUE(win, NS_ERROR_UNEXPECTED); + if (NS_FAILED(win->UnregisterIdleObserver(aIdleObserver))) { + NS_WARNING("Failed to remove idle observer."); + } + return NS_OK; +} + NS_IMETHODIMP Navigator::MozVibrate(const jsval& aPattern, JSContext* cx) { diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index b4be4a211aa..02b78910f1e 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -20,6 +20,7 @@ #include "nsDOMStorage.h" #include "nsDOMOfflineResourceList.h" #include "nsDOMError.h" +#include "nsIIdleService.h" #ifdef XP_WIN #ifdef GetClassName @@ -220,6 +221,7 @@ #include "nsWrapperCacheInlines.h" #include "nsDOMEventTargetHelper.h" #include "nsIAppsService.h" +#include "prrng.h" #ifdef ANDROID #include @@ -238,6 +240,7 @@ using mozilla::TimeDuration; nsGlobalWindow::WindowByIdTable *nsGlobalWindow::sWindowsById = nsnull; bool nsGlobalWindow::sWarnedAboutWindowInternal = false; +bool nsGlobalWindow::sIdleObserversAPIFuzzTimeDisabled = false; static nsIEntropyCollector *gEntropyCollector = nsnull; static PRInt32 gRefCnt = 0; @@ -495,7 +498,9 @@ nsDOMMozURLProperty::RevokeObjectURL(const nsAString& aURL) * An indirect observer object that means we don't have to implement nsIObserver * on nsGlobalWindow, where any script could see it. */ -class nsGlobalWindowObserver MOZ_FINAL : public nsIObserver { +class nsGlobalWindowObserver MOZ_FINAL : public nsIObserver, + public nsIInterfaceRequestor +{ public: nsGlobalWindowObserver(nsGlobalWindow* aWindow) : mWindow(aWindow) {} NS_DECL_ISUPPORTS @@ -506,11 +511,19 @@ public: return mWindow->Observe(aSubject, aTopic, aData); } void Forget() { mWindow = nsnull; } + NS_IMETHODIMP GetInterface(const nsIID& aIID, void** aResult) + { + if (mWindow && aIID.Equals(NS_GET_IID(nsIDOMWindow)) && mWindow) { + return mWindow->QueryInterface(aIID, aResult); + } + return NS_NOINTERFACE; + } + private: nsGlobalWindow* mWindow; }; -NS_IMPL_ISUPPORTS1(nsGlobalWindowObserver, nsIObserver) +NS_IMPL_ISUPPORTS2(nsGlobalWindowObserver, nsIObserver, nsIInterfaceRequestor) nsTimeout::nsTimeout() { @@ -628,15 +641,21 @@ NewOuterWindowProxy(JSContext *cx, JSObject *parent) nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow) : nsPIDOMWindow(aOuterWindow), + mIdleFuzzFactor(0), + mIdleCallbackIndex(-1), + mCurrentlyIdle(false), + mAddActiveEventFuzzTime(true), mIsFrozen(false), mFullScreen(false), - mIsClosed(false), - mInClose(false), + mIsClosed(false), + mInClose(false), mHavePendingClose(false), mHadOriginalOpener(false), mIsPopupSpam(false), mBlockScriptedClosingFlag(false), mFireOfflineStatusChangeEventOnThaw(false), + mNotifyIdleObserversIdleOnThaw(false), + mNotifyIdleObserversActiveOnThaw(false), mCreatingInnerWindow(false), mIsChrome(false), mCleanMessageManager(false), @@ -723,6 +742,9 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow) Preferences::AddIntVarCache(&gMinBackgroundTimeoutValue, "dom.min_background_timeout_value", DEFAULT_MIN_BACKGROUND_TIMEOUT_VALUE); + Preferences::AddBoolVarCache(&sIdleObserversAPIFuzzTimeDisabled, + "dom.idle-observers-api.fuzz_time.disabled", + false); } if (gDumpFile == nsnull) { @@ -956,7 +978,7 @@ nsGlobalWindow::CleanUp(bool aIgnoreModalDialog) if (mCleanedUp) return; mCleanedUp = true; - + mEventTargetObjects.EnumerateEntries(DisconnectEventTargetObjects, nsnull); mEventTargetObjects.Clear(); @@ -967,6 +989,10 @@ nsGlobalWindow::CleanUp(bool aIgnoreModalDialog) os->RemoveObserver(mObserver, "dom-storage2-changed"); } + if (mIdleService) { + mIdleService->RemoveIdleObserver(mObserver, MIN_IDLE_NOTIFICATION_TIME_S); + } + // Drop its reference to this dying window, in case for some bogus reason // the object stays around. mObserver->Forget(); @@ -1024,6 +1050,11 @@ nsGlobalWindow::CleanUp(bool aIgnoreModalDialog) CleanupCachedXBLHandlers(this); + if (mIdleTimer) { + mIdleTimer->Cancel(); + mIdleTimer = nsnull; + } + #ifdef DEBUG nsCycleCollector_DEBUG_shouldBeFreed(static_cast(this)); #endif @@ -1073,6 +1104,13 @@ nsGlobalWindow::FreeInnerObjects() ClearAllTimeouts(); + if (mIdleTimer) { + mIdleTimer->Cancel(); + mIdleTimer = nsnull; + } + + mIdleObservers.Clear(); + mChromeEventHandler = nsnull; if (mListenerManager) { @@ -1236,6 +1274,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsGlobalWindow) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mApplicationCache) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDocumentPrincipal) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDoc) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mIdleService) // Traverse stuff from nsPIDOMWindow NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mChromeEventHandler) @@ -1247,6 +1286,10 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsGlobalWindow) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mPendingStorageEvents) + for (PRUint32 i = 0; i < tmp->mIdleObservers.Length(); i++) { + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mIdleObservers[i].nsIIdleObserverPtr"); + cb.NoteXPCOMChild(tmp->mIdleObservers.ElementAt(i).mIdleObserver.get()); + } NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindow) @@ -1282,9 +1325,12 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindow) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mFrameElement) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mFocusedNode) + NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mIdleService) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mPendingStorageEvents) + NS_IMPL_CYCLE_COLLECTION_UNLINK_NSTARRAY(mIdleObservers) + NS_IMPL_CYCLE_COLLECTION_UNLINK_END struct TraceData @@ -2472,6 +2518,15 @@ nsGlobalWindow::PreHandleEvent(nsEventChainPreVisitor& aVisitor) } aVisitor.mParentTarget = GetParentTarget(); + + // Handle 'active' event. + if (!mIdleObservers.IsEmpty() && + NS_IS_TRUSTED_EVENT(aVisitor.mEvent) && + (NS_IS_MOUSE_EVENT(aVisitor.mEvent) || + NS_IS_DRAG_EVENT(aVisitor.mEvent))) { + mAddActiveEventFuzzTime = false; + } + return NS_OK; } @@ -8380,6 +8435,408 @@ nsGlobalWindow::FireOfflineStatusEvent() nsContentUtils::DispatchTrustedEvent(mDoc, eventTarget, name, true, false); } +class NotifyIdleObserverRunnable : public nsRunnable +{ +public: + NotifyIdleObserverRunnable(nsIIdleObserver* aIdleObserver, + PRUint32 aTimeInS, + bool aCallOnidle, + nsGlobalWindow* aIdleWindow) + : mIdleObserver(aIdleObserver), mTimeInS(aTimeInS), mIdleWindow(aIdleWindow), + mCallOnidle(aCallOnidle) + { } + + NS_IMETHOD Run() + { + if (mIdleWindow->ContainsIdleObserver(mIdleObserver, mTimeInS)) { + return mCallOnidle ? mIdleObserver->Onidle() : mIdleObserver->Onactive(); + } + return NS_OK; + } + +private: + nsCOMPtr mIdleObserver; + PRUint32 mTimeInS; + nsRefPtr mIdleWindow; + + // If false then call on active + bool mCallOnidle; +}; + +void +nsGlobalWindow::NotifyIdleObserver(nsIIdleObserver* aIdleObserver, + PRUint32 aIdleObserverTimeInS, + bool aCallOnidle) +{ + nsCOMPtr caller = + new NotifyIdleObserverRunnable(aIdleObserver, aIdleObserverTimeInS, + aCallOnidle, this); + if (NS_FAILED(NS_DispatchToCurrentThread(caller))) { + NS_WARNING("Failed to dispatch thread for idle observer notification."); + } +} + +bool +nsGlobalWindow::ContainsIdleObserver(nsIIdleObserver* aIdleObserver, PRUint32 aTimeInS) +{ + MOZ_ASSERT(aIdleObserver, "Idle observer not instantiated."); + bool found = false; + nsTObserverArray::ForwardIterator iter(mIdleObservers); + while (iter.HasMore()) { + IdleObserverHolder& idleObserver = iter.GetNext(); + if (idleObserver.mIdleObserver == aIdleObserver && + idleObserver.mTimeInS == aTimeInS) { + found = true; + break; + } + } + return found; +} + +void +IdleActiveTimerCallback(nsITimer* aTimer, void* aClosure) +{ + nsRefPtr idleWindow = static_cast(aClosure); + MOZ_ASSERT(idleWindow, "Idle window has not been instantiated."); + idleWindow->NotifyIdleObserversOfIdleActiveEvent(); +} + +void +IdleObserverTimerCallback(nsITimer* aTimer, void* aClosure) +{ + nsRefPtr idleWindow = static_cast(aClosure); + MOZ_ASSERT(idleWindow, "Idle window has not been instantiated."); + idleWindow->HandleIdleObserverCallback(); +} + +void +nsGlobalWindow::HandleIdleObserverCallback() +{ + MOZ_ASSERT(IsInnerWindow(), "Must be an inner window!"); + MOZ_ASSERT(static_cast(mIdleCallbackIndex) < mIdleObservers.Length(), + "Idle callback index exceeds array bounds!"); + IdleObserverHolder idleObserver = + mIdleObservers.ElementAt(mIdleCallbackIndex); + NotifyIdleObserver(idleObserver.mIdleObserver, + idleObserver.mTimeInS, + true); + mIdleCallbackIndex++; + if (NS_FAILED(ScheduleNextIdleObserverCallback())) { + NS_WARNING("Failed to set next idle observer callback."); + } +} + +nsresult +nsGlobalWindow::ScheduleNextIdleObserverCallback() +{ + MOZ_ASSERT(IsInnerWindow(), "Must be an inner window!"); + MOZ_ASSERT(mIdleService, "No idle service!"); + + if (mIdleCallbackIndex < 0 || + static_cast(mIdleCallbackIndex) >= mIdleObservers.Length()) { + return NS_OK; + } + + IdleObserverHolder& idleObserver = + mIdleObservers.ElementAt(mIdleCallbackIndex); + + PRUint32 userIdleTimeMS = 0; + nsresult rv = mIdleService->GetIdleTime(&userIdleTimeMS); + NS_ENSURE_SUCCESS(rv, rv); + + PRUint32 callbackTimeMS = 0; + if (idleObserver.mTimeInS * 1000 > userIdleTimeMS) { + callbackTimeMS = idleObserver.mTimeInS * 1000 - userIdleTimeMS; + } + + mIdleTimer->Cancel(); + rv = mIdleTimer->InitWithFuncCallback(IdleObserverTimerCallback, + this, + callbackTimeMS, + nsITimer::TYPE_ONE_SHOT); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +PRUint32 +nsGlobalWindow::GetFuzzTimeMS() +{ + MOZ_ASSERT(IsInnerWindow(), "Must be an inner window!"); + + if (sIdleObserversAPIFuzzTimeDisabled) { + return 0; + } + + PRUint32 randNum = 0; + PRSize nbytes = PR_GetRandomNoise(&randNum, sizeof(randNum)); + if (nbytes != sizeof(randNum)) { + NS_WARNING("PR_GetRandomNoise(...) Not implemented or no available noise!"); + return MAX_IDLE_FUZZ_TIME_MS; + } + + if (randNum > MAX_IDLE_FUZZ_TIME_MS) { + (randNum) %= MAX_IDLE_FUZZ_TIME_MS; + } + + return randNum; +} + +nsresult +nsGlobalWindow::ScheduleActiveTimerCallback() +{ + MOZ_ASSERT(IsInnerWindow(), "Must be an inner window!"); + + if (!mAddActiveEventFuzzTime) { + return NotifyIdleObserversOfIdleActiveEvent(); + } + + nsRefPtr kungFuDeathGrip(this); + + MOZ_ASSERT(mIdleTimer); + mIdleTimer->Cancel(); + + PRUint32 fuzzFactorInMS = GetFuzzTimeMS(); + nsresult rv = mIdleTimer->InitWithFuncCallback(IdleActiveTimerCallback, + this, + fuzzFactorInMS, + nsITimer::TYPE_ONE_SHOT); + NS_ENSURE_SUCCESS(rv, rv); + return NS_OK; +} + +nsresult +nsGlobalWindow::ScheduleIdleTimerCallback() +{ + MOZ_ASSERT(IsInnerWindow(), "Must be an inner window!"); + MOZ_ASSERT(mIdleTimer); + + nsRefPtr kungFuDeathGrip(this); + mIdleTimer->Cancel(); + mIdleFuzzFactor = GetFuzzTimeMS(); + nsresult rv = mIdleTimer->InitWithFuncCallback(IdleActiveTimerCallback, + this, + mIdleFuzzFactor, + nsITimer::TYPE_ONE_SHOT); + NS_ENSURE_SUCCESS(rv, rv); + return NS_OK; +} + +nsresult +nsGlobalWindow::NotifyIdleObserversOfIdleActiveEvent() +{ + MOZ_ASSERT(IsInnerWindow(), "Must be an inner window!"); + + if (mCurrentlyIdle) { + mIdleCallbackIndex = 0; + nsresult rv = ScheduleNextIdleObserverCallback(); + NS_ENSURE_SUCCESS(rv, rv); + return NS_OK; + } + + mIdleCallbackIndex = -1; + nsTObserverArray::ForwardIterator iter(mIdleObservers); + while (iter.HasMore()) { + IdleObserverHolder& idleObserver = iter.GetNext(); + NotifyIdleObserver(idleObserver.mIdleObserver, idleObserver.mTimeInS, false); + } + + return NS_OK; +} + +PRUint32 +nsGlobalWindow::FindInsertionIndex(IdleObserverHolder* aIdleObserver) +{ + MOZ_ASSERT(aIdleObserver, "Idle observer not instantiated."); + + PRUint32 i = 0; + nsTObserverArray::ForwardIterator iter(mIdleObservers); + while (iter.HasMore()) { + IdleObserverHolder& idleObserver = iter.GetNext(); + if (idleObserver.mTimeInS > aIdleObserver->mTimeInS) { + break; + } + i++; + MOZ_ASSERT(i <= mIdleObservers.Length(), "Array index out of bounds error."); + } + + return i; +} + +nsresult +nsGlobalWindow::RegisterIdleObserver(nsIIdleObserver* aIdleObserver) +{ + MOZ_ASSERT(IsInnerWindow(), "Must be an inner window!"); + + nsresult rv; + if (mIdleObservers.IsEmpty()) { + mIdleService = do_GetService("@mozilla.org/widget/idleservice;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mIdleService->AddIdleObserver(mObserver, MIN_IDLE_NOTIFICATION_TIME_S); + NS_ENSURE_SUCCESS(rv, rv); + + if (!mIdleTimer) { + mIdleTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + } else { + mIdleTimer->Cancel(); + } + + mIdleFuzzFactor = GetFuzzTimeMS(); + } + + MOZ_ASSERT(mIdleService); + MOZ_ASSERT(mIdleTimer); + + IdleObserverHolder tmpIdleObserver; + tmpIdleObserver.mIdleObserver = aIdleObserver; + rv = aIdleObserver->GetTime(&tmpIdleObserver.mTimeInS); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_ARG_MAX(tmpIdleObserver.mTimeInS, PR_UINT32_MAX / 1000); + NS_ENSURE_ARG_MIN(tmpIdleObserver.mTimeInS, MIN_IDLE_NOTIFICATION_TIME_S); + + PRUint32 insertAtIndex = FindInsertionIndex(&tmpIdleObserver); + if (insertAtIndex == mIdleObservers.Length()) { + mIdleObservers.AppendElement(tmpIdleObserver); + } + else { + mIdleObservers.InsertElementAt(insertAtIndex, tmpIdleObserver); + } + + bool userIsIdle = false; + rv = nsContentUtils::IsUserIdle(MIN_IDLE_NOTIFICATION_TIME_S, &userIsIdle); + NS_ENSURE_SUCCESS(rv, rv); + + // Special case. First idle observer added to empty list while the user is idle. + // Haven't received 'idle' topic notification from slow idle service yet. + // Need to wait for the idle notification and then notify idle observers in the list. + if (userIsIdle && mIdleCallbackIndex == -1) { + return NS_OK; + } + + MOZ_ASSERT(mIdleCallbackIndex >= 0); + + if (!mCurrentlyIdle) { + return NS_OK; + } + + if (static_cast(insertAtIndex) < mIdleCallbackIndex) { + NotifyIdleObserver(tmpIdleObserver.mIdleObserver, + tmpIdleObserver.mTimeInS, + true); + mIdleCallbackIndex++; + return NS_OK; + } + + if (static_cast(insertAtIndex) == mIdleCallbackIndex) { + PRUint32 userIdleTimeMS; + rv = mIdleService->GetIdleTime(&userIdleTimeMS); + NS_ENSURE_SUCCESS(rv, rv); + + if (tmpIdleObserver.mTimeInS*1000 <= userIdleTimeMS) { + NotifyIdleObserver(tmpIdleObserver.mIdleObserver, + tmpIdleObserver.mTimeInS, + true); + mIdleCallbackIndex++; + return NS_OK; + } + + mIdleTimer->Cancel(); + + rv = ScheduleNextIdleObserverCallback(); + NS_ENSURE_SUCCESS(rv, rv); + } + return NS_OK; +} + +nsresult +nsGlobalWindow::FindIndexOfElementToRemove(nsIIdleObserver* aIdleObserver, + PRInt32* aRemoveElementIndex) +{ + MOZ_ASSERT(IsInnerWindow(), "Must be an inner window!"); + MOZ_ASSERT(aIdleObserver, "Idle observer not instantiated."); + + *aRemoveElementIndex = 0; + if (mIdleObservers.IsEmpty()) { + return NS_ERROR_FAILURE; + } + + PRUint32 aIdleObserverTimeInS; + nsresult rv = aIdleObserver->GetTime(&aIdleObserverTimeInS); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_ARG_MIN(aIdleObserverTimeInS, MIN_IDLE_NOTIFICATION_TIME_S); + + nsTObserverArray::ForwardIterator iter(mIdleObservers); + while (iter.HasMore()) { + IdleObserverHolder& idleObserver = iter.GetNext(); + if (idleObserver.mTimeInS == aIdleObserverTimeInS && + idleObserver.mIdleObserver == aIdleObserver ) { + break; + } + (*aRemoveElementIndex)++; + } + return static_cast(*aRemoveElementIndex) >= mIdleObservers.Length() ? + NS_ERROR_FAILURE : NS_OK; +} + +nsresult +nsGlobalWindow::UnregisterIdleObserver(nsIIdleObserver* aIdleObserver) +{ + MOZ_ASSERT(IsInnerWindow(), "Must be an inner window!"); + MOZ_ASSERT(mIdleTimer); + + PRInt32 removeElementIndex; + nsresult rv = FindIndexOfElementToRemove(aIdleObserver, &removeElementIndex); + if (NS_FAILED(rv)) { + NS_WARNING("Idle observer not found in list of idle observers. No idle observer removed."); + return NS_OK; + } + mIdleObservers.RemoveElementAt(removeElementIndex); + + if (mIdleObservers.IsEmpty() && mIdleService) { + rv = mIdleService->RemoveIdleObserver(mObserver, MIN_IDLE_NOTIFICATION_TIME_S); + NS_ENSURE_SUCCESS(rv, rv); + mIdleService = nsnull; + + mIdleCallbackIndex = -1; + return NS_OK; + } + + if (!mCurrentlyIdle) { + return NS_OK; + } + + if (removeElementIndex < mIdleCallbackIndex) { + mIdleCallbackIndex--; + return NS_OK; + } + + if (removeElementIndex != mIdleCallbackIndex) { + return NS_OK; + } + + nsRefPtr kungFuDeathGrip(this); + + mIdleTimer->Cancel(); + + // If the last element in the array had been notified then decrement + // mIdleCallbackIndex because an idle was removed from the list of + // idle observers. + // Example: add idle observer with time 1, 2, 3, + // Idle notifications for idle observers with time 1, 2, 3 are complete + // Remove idle observer with time 3 while the user is still idle. + // The user never transitioned to active state. + // Add an idle observer with idle time 4 + if (static_cast(mIdleCallbackIndex) == mIdleObservers.Length()) { + mIdleCallbackIndex--; + } + rv = ScheduleNextIdleObserverCallback(); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + nsresult nsGlobalWindow::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aData) @@ -8395,6 +8852,29 @@ nsGlobalWindow::Observe(nsISupports* aSubject, const char* aTopic, return NS_OK; } + if (!nsCRT::strcmp(aTopic, OBSERVER_TOPIC_IDLE)) { + mCurrentlyIdle = true; + if (IsFrozen()) { + // need to fire only one idle event while the window is frozen. + mNotifyIdleObserversIdleOnThaw = true; + mNotifyIdleObserversActiveOnThaw = false; + } else if (mOuterWindow && mOuterWindow->GetCurrentInnerWindow() == this) { + ScheduleIdleTimerCallback(); + } + return NS_OK; + } + + if (!nsCRT::strcmp(aTopic, OBSERVER_TOPIC_ACTIVE)) { + mCurrentlyIdle = false; + if (IsFrozen()) { + mNotifyIdleObserversActiveOnThaw = true; + mNotifyIdleObserversIdleOnThaw = false; + } else if (mOuterWindow && mOuterWindow->GetCurrentInnerWindow() == this) { + ScheduleActiveTimerCallback(); + } + return NS_OK; + } + if (IsInnerWindow() && !nsCRT::strcmp(aTopic, "dom-storage2-changed")) { nsIPrincipal *principal; nsresult rv; @@ -8563,6 +9043,16 @@ nsGlobalWindow::FireDelayedDOMEvents() FireOfflineStatusEvent(); } + if (mNotifyIdleObserversIdleOnThaw) { + mNotifyIdleObserversIdleOnThaw = false; + ScheduleIdleTimerCallback(); + } + + if (mNotifyIdleObserversActiveOnThaw) { + mNotifyIdleObserversActiveOnThaw = false; + ScheduleActiveTimerCallback(); + } + nsCOMPtr node = do_QueryInterface(GetDocShell()); if (node) { diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h index 0b77d008697..7aa2e21512f 100644 --- a/dom/base/nsGlobalWindow.h +++ b/dom/base/nsGlobalWindow.h @@ -64,6 +64,7 @@ #include "nsIInlineEventHandlers.h" #include "nsWrapperCacheInlines.h" #include "nsIDOMApplicationRegistry.h" +#include "nsIIdleObserver.h" // JS includes #include "jsapi.h" @@ -79,6 +80,12 @@ // dialogs up to this limit, even if they were disabled. #define MAX_DIALOG_COUNT 10 +// Idle fuzz time upper limit +#define MAX_IDLE_FUZZ_TIME_MS 90000 + +// Min idle notification time in seconds. +#define MIN_IDLE_NOTIFICATION_TIME_S 1 + class nsIDOMBarProp; class nsIDocument; class nsPresContext; @@ -101,6 +108,7 @@ class nsDOMEventTargetHelper; class nsDOMOfflineResourceList; class nsDOMMozURLProperty; class nsDOMWindowUtils; +class nsIIdleService; #ifdef MOZ_DISABLE_DOMCRYPTO class nsIDOMCrypto; @@ -195,6 +203,35 @@ private: nsAutoRefCnt mRefCnt; }; +struct IdleObserverHolder +{ + nsCOMPtr mIdleObserver; + PRUint32 mTimeInS; + + IdleObserverHolder() + : mTimeInS(0) + { + MOZ_COUNT_CTOR(IdleObserverHolder); + } + + IdleObserverHolder(const IdleObserverHolder& aOtherIdleObserver) + : mIdleObserver(aOtherIdleObserver.mIdleObserver), mTimeInS(aOtherIdleObserver.mTimeInS) + { + MOZ_COUNT_CTOR(IdleObserverHolder); + } + + bool operator==(const IdleObserverHolder& aOtherIdleObserver) const { + return + mIdleObserver == aOtherIdleObserver.mIdleObserver && + mTimeInS == aOtherIdleObserver.mTimeInS; + } + + ~IdleObserverHolder() + { + MOZ_COUNT_DTOR(IdleObserverHolder); + } +}; + //***************************************************************************** // nsGlobalWindow: Global Object for Scripting //***************************************************************************** @@ -546,7 +583,39 @@ public: void AddEventTargetObject(nsDOMEventTargetHelper* aObject); void RemoveEventTargetObject(nsDOMEventTargetHelper* aObject); + void NotifyIdleObserver(nsIIdleObserver* aIdleObserver, + PRUint32 aIdleObserverTimeInS, + bool aCallOnidle); + nsresult NotifyIdleObserversOfIdleActiveEvent(); + bool ContainsIdleObserver(nsIIdleObserver* aIdleObserver, PRUint32 timeInS); + void HandleIdleObserverCallback(); + protected: + // Array of idle observers that are notified of idle events. + nsTObserverArray mIdleObservers; + + // Idle timer used for function callbacks to notify idle observers. + nsCOMPtr mIdleTimer; + + // Idle fuzz time added to idle timer callbacks. + PRUint32 mIdleFuzzFactor; + + // Index in mArrayIdleObservers + // Next idle observer to notify user idle status + PRInt32 mIdleCallbackIndex; + + // If false then the topic is "active" + // If true then the topic is "idle" + bool mCurrentlyIdle; + + // Set to true when a fuzz time needs to be applied + // to active notifications to the idle observer. + bool mAddActiveEventFuzzTime; + + nsCOMPtr mIdleService; + + static bool sIdleObserversAPIFuzzTimeDisabled; + friend class HashchangeCallback; friend class nsBarProp; @@ -696,6 +765,17 @@ protected: const nsAString &aPopupWindowName, const nsAString &aPopupWindowFeatures); void FireOfflineStatusEvent(); + + nsresult ScheduleNextIdleObserverCallback(); + PRUint32 GetFuzzTimeMS(); + nsresult ScheduleActiveTimerCallback(); + nsresult ScheduleIdleTimerCallback(); + PRUint32 FindInsertionIndex(IdleObserverHolder* aIdleObserver); + virtual nsresult RegisterIdleObserver(nsIIdleObserver* aIdleObserverPtr); + nsresult FindIndexOfElementToRemove(nsIIdleObserver* aIdleObserver, + PRInt32* aRemoveElementIndex); + virtual nsresult UnregisterIdleObserver(nsIIdleObserver* aIdleObserverPtr); + nsresult FireHashchange(const nsAString &aOldURL, const nsAString &aNewURL); void FlushPendingNotifications(mozFlushType aType); @@ -721,7 +801,7 @@ protected: nsresult GetScrollXY(PRInt32* aScrollX, PRInt32* aScrollY, bool aDoFlush); nsresult GetScrollMaxXY(PRInt32* aScrollMaxX, PRInt32* aScrollMaxY); - + nsresult GetOuterSize(nsIntSize* aSizeCSSPixels); nsresult SetOuterSize(PRInt32 aLengthCSSPixels, bool aIsWidth); nsRect GetInnerScreenRect(); @@ -798,7 +878,7 @@ protected: static void NotifyDOMWindowFrozen(nsGlobalWindow* aWindow); static void NotifyDOMWindowThawed(nsGlobalWindow* aWindow); - + void ClearStatus(); virtual void UpdateParentTarget(); @@ -832,7 +912,7 @@ protected: // where we don't want to force creation of a new inner window since // we're in the middle of doing just that. bool mIsFrozen : 1; - + // These members are only used on outer window objects. Make sure // you never set any of these on an inner object! bool mFullScreen : 1; @@ -850,6 +930,8 @@ protected: // Track what sorts of events we need to fire when thawed bool mFireOfflineStatusChangeEventOnThaw : 1; + bool mNotifyIdleObserversIdleOnThaw : 1; + bool mNotifyIdleObserversActiveOnThaw : 1; // Indicates whether we're in the middle of creating an initializing // a new inner window object. diff --git a/dom/base/nsPIDOMWindow.h b/dom/base/nsPIDOMWindow.h index 6a21b4b4c4f..8c3867f0c96 100644 --- a/dom/base/nsPIDOMWindow.h +++ b/dom/base/nsPIDOMWindow.h @@ -16,13 +16,13 @@ #include "nsIDOMEventTarget.h" #include "nsIDOMDocument.h" #include "nsCOMPtr.h" -#include "nsEvent.h" #include "nsIURI.h" #define DOM_WINDOW_DESTROYED_TOPIC "dom-window-destroyed" #define DOM_WINDOW_FROZEN_TOPIC "dom-window-frozen" #define DOM_WINDOW_THAWED_TOPIC "dom-window-thawed" +class nsIIdleObserver; class nsIPrincipal; // Popup control state enum. The values in this enum must go from most @@ -48,8 +48,8 @@ class nsIArray; class nsPIWindowRoot; #define NS_PIDOMWINDOW_IID \ -{ 0x41dd6a62, 0xda59, 0x46e5, \ - { 0x9d, 0x74, 0x45, 0xf4, 0x49, 0x4e, 0x1a, 0x70 } } +{ 0x0c4d0b84, 0xb524, 0x4572, \ + { 0x8e, 0xd1, 0x7f, 0x78, 0x14, 0x7c, 0x4d, 0xf1 } } class nsPIDOMWindow : public nsIDOMWindowInternal { @@ -70,6 +70,9 @@ public: mIsActive = aActive; } + virtual nsresult RegisterIdleObserver(nsIIdleObserver* aIdleObserver) = 0; + virtual nsresult UnregisterIdleObserver(nsIIdleObserver* aIdleObserver) = 0; + bool IsActive() { NS_PRECONDITION(IsOuterWindow(), diff --git a/dom/base/test/Makefile.in b/dom/base/test/Makefile.in index a49addfa8b0..624fc5639d4 100644 --- a/dom/base/test/Makefile.in +++ b/dom/base/test/Makefile.in @@ -19,6 +19,14 @@ TEST_FILES = \ test_screen_orientation.html \ $(NULL) +CHROME_TEST_FILES = \ + test_bug715041.xul \ + test_bug715041_removal.xul \ + $(NULL) + libs:: $(TEST_FILES) $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir) +libs:: $(CHROME_TEST_FILES) + $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir) + diff --git a/dom/base/test/test_bug715041.xul b/dom/base/test/test_bug715041.xul new file mode 100644 index 00000000000..a84358ec545 --- /dev/null +++ b/dom/base/test/test_bug715041.xul @@ -0,0 +1,760 @@ + + + + + + + + diff --git a/dom/base/test/test_bug715041_removal.xul b/dom/base/test/test_bug715041_removal.xul new file mode 100644 index 00000000000..2b77dd5949d --- /dev/null +++ b/dom/base/test/test_bug715041_removal.xul @@ -0,0 +1,845 @@ + + + + + + + + diff --git a/dom/interfaces/base/Makefile.in b/dom/interfaces/base/Makefile.in index 5bb6d0bee0d..208b009bd53 100644 --- a/dom/interfaces/base/Makefile.in +++ b/dom/interfaces/base/Makefile.in @@ -57,6 +57,7 @@ XPIDLSRCS = \ nsIDOMPerformance.idl \ nsIDOMPerformanceTiming.idl \ nsIDOMPerformanceNavigation.idl \ + nsIIdleObserver.idl \ $(NULL) include $(topsrcdir)/config/rules.mk diff --git a/dom/interfaces/base/nsIDOMNavigator.idl b/dom/interfaces/base/nsIDOMNavigator.idl index 6f8b48d1d7b..d7a7c18c1ed 100644 --- a/dom/interfaces/base/nsIDOMNavigator.idl +++ b/dom/interfaces/base/nsIDOMNavigator.idl @@ -5,7 +5,9 @@ #include "domstubs.idl" -[scriptable, uuid(e0737ed5-89c5-4fe3-891e-a75bf3a1bb55)] +interface nsIIdleObserver; + +[scriptable, uuid(c148ed5a-31c1-4a21-a13b-341f592579ff)] interface nsIDOMNavigator : nsISupports { readonly attribute DOMString appCodeName; @@ -78,6 +80,16 @@ interface nsIDOMNavigator : nsISupports [implicit_jscontext] void mozVibrate(in jsval aPattern); + /** + * Navigator requests to add an idle observer to the existing window. + */ + void addIdleObserver(in nsIIdleObserver aIdleObserver); + + /** + * Navigator requests to remove an idle observer from the existing window. + */ + void removeIdleObserver(in nsIIdleObserver aIdleObserver); + /** * Request a wake lock for a resource. * diff --git a/dom/interfaces/base/nsIIdleObserver.idl b/dom/interfaces/base/nsIIdleObserver.idl new file mode 100644 index 00000000000..e7a31c31924 --- /dev/null +++ b/dom/interfaces/base/nsIIdleObserver.idl @@ -0,0 +1,16 @@ +/* -*- Mode: IDL; tab-width: 2; 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 "domstubs.idl" + +[scriptable, uuid(37916e05-e062-4f72-96d5-660cfb55e9b6)] +interface nsIIdleObserver : nsISupports +{ + // Time is in seconds and is read only when idle observers are added + // and removed. + readonly attribute unsigned long time; + void onidle(); + void onactive(); +}; diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index 337e8f6597b..1cfe2f57d81 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -3545,6 +3545,9 @@ pref("full-screen-api.allow-trusted-requests-only", true); pref("full-screen-api.exit-on-deactivate", true); pref("full-screen-api.pointer-lock.enabled", true); +// DOM idle observers API +pref("dom.idle-observers-api.enabled", true); + // Time limit, in milliseconds, for nsEventStateManager::IsHandlingUserInput(). // Used to detect long running handlers of user-generated events. pref("dom.event.handling-user-input-time-limit", 1000); diff --git a/widget/Makefile.in b/widget/Makefile.in index f75be42c723..6f38c0db63c 100644 --- a/widget/Makefile.in +++ b/widget/Makefile.in @@ -119,6 +119,7 @@ XPIDLSRCS = \ nsIPrintSettingsService.idl \ nsIPrintOptions.idl \ nsIIdleService.idl \ + nsIIdleServiceInternal.idl \ nsIGfxInfo.idl \ nsIGfxInfoDebug.idl \ nsIIMEPicker.idl \ diff --git a/widget/android/nsIdleServiceAndroid.cpp b/widget/android/nsIdleServiceAndroid.cpp index 46eb4e52ad8..17435e5644d 100644 --- a/widget/android/nsIdleServiceAndroid.cpp +++ b/widget/android/nsIdleServiceAndroid.cpp @@ -8,7 +8,7 @@ #include "nsIdleServiceAndroid.h" #include "nsIServiceManager.h" -NS_IMPL_ISUPPORTS2(nsIdleServiceAndroid, nsIIdleService, nsIdleService) +NS_IMPL_ISUPPORTS_INHERITED0(nsIdleServiceAndroid, nsIdleService) bool nsIdleServiceAndroid::PollIdleTime(PRUint32 *aIdleTime) diff --git a/widget/android/nsIdleServiceAndroid.h b/widget/android/nsIdleServiceAndroid.h index 7e4708975d5..92f4f89347d 100644 --- a/widget/android/nsIdleServiceAndroid.h +++ b/widget/android/nsIdleServiceAndroid.h @@ -13,10 +13,25 @@ class nsIdleServiceAndroid : public nsIdleService { public: - NS_DECL_ISUPPORTS + NS_DECL_ISUPPORTS_INHERITED bool PollIdleTime(PRUint32* aIdleTime); + + static already_AddRefed GetInstance() + { + nsIdleServiceAndroid* idleService = + static_cast(nsIdleService::GetInstance().get()); + if (!idleService) { + idleService = new nsIdleServiceAndroid(); + NS_ADDREF(idleService); + } + + return idleService; + } + protected: + nsIdleServiceAndroid() { } + virtual ~nsIdleServiceAndroid() { } bool UsePollMode(); }; diff --git a/widget/android/nsWidgetFactory.cpp b/widget/android/nsWidgetFactory.cpp index 25564012c04..a8575cef10c 100644 --- a/widget/android/nsWidgetFactory.cpp +++ b/widget/android/nsWidgetFactory.cpp @@ -30,7 +30,7 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsWindow) NS_GENERIC_FACTORY_CONSTRUCTOR(nsScreenManagerAndroid) -NS_GENERIC_FACTORY_CONSTRUCTOR(nsIdleServiceAndroid) +NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIdleServiceAndroid, nsIdleServiceAndroid::GetInstance) NS_GENERIC_FACTORY_CONSTRUCTOR(nsTransferable) NS_GENERIC_FACTORY_CONSTRUCTOR(nsClipboard) NS_GENERIC_FACTORY_CONSTRUCTOR(nsClipboardHelper) diff --git a/widget/android/nsWindow.cpp b/widget/android/nsWindow.cpp index f18f6d0414e..6db23c40698 100644 --- a/widget/android/nsWindow.cpp +++ b/widget/android/nsWindow.cpp @@ -2060,7 +2060,7 @@ nsWindow::UserActivity() } if (mIdleService) { - mIdleService->ResetIdleTimeOut(); + mIdleService->ResetIdleTimeOut(0); } } diff --git a/widget/android/nsWindow.h b/widget/android/nsWindow.h index afc1632529b..debb4c63002 100644 --- a/widget/android/nsWindow.h +++ b/widget/android/nsWindow.h @@ -8,7 +8,7 @@ #include "nsBaseWidget.h" #include "gfxPoint.h" - +#include "nsIIdleServiceInternal.h" #include "nsTArray.h" #ifdef MOZ_JAVA_COMPOSITOR @@ -17,7 +17,6 @@ #endif class gfxASurface; -class nsIdleService; struct ANPEvent; @@ -188,7 +187,7 @@ protected: double mSwipeMaxPinchDelta; double mSwipeMinDistance; - nsCOMPtr mIdleService; + nsCOMPtr mIdleService; bool mIMEComposing; nsString mIMEComposingText; diff --git a/widget/cocoa/nsIdleServiceX.h b/widget/cocoa/nsIdleServiceX.h index 6bdb47f50b8..e8bc1cc0a57 100644 --- a/widget/cocoa/nsIdleServiceX.h +++ b/widget/cocoa/nsIdleServiceX.h @@ -10,14 +10,25 @@ class nsIdleServiceX : public nsIdleService { public: - NS_DECL_ISUPPORTS - - nsIdleServiceX() {} - virtual ~nsIdleServiceX() {} + NS_DECL_ISUPPORTS_INHERITED bool PollIdleTime(PRUint32* aIdleTime); + static already_AddRefed GetInstance() + { + nsIdleServiceX* idleService = + static_cast(nsIdleService::GetInstance().get()); + if (!idleService) { + idleService = new nsIdleServiceX(); + NS_ADDREF(idleService); + } + + return idleService; + } + protected: + nsIdleServiceX() { } + virtual ~nsIdleServiceX() { } bool UsePollMode(); }; diff --git a/widget/cocoa/nsWidgetFactory.mm b/widget/cocoa/nsWidgetFactory.mm index 718b6d395a6..6f7d0f36fb9 100644 --- a/widget/cocoa/nsWidgetFactory.mm +++ b/widget/cocoa/nsWidgetFactory.mm @@ -48,7 +48,7 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsDeviceContextSpecX) NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrintOptionsX, Init) NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrintDialogServiceX, Init) NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrintSession, Init) -NS_GENERIC_FACTORY_CONSTRUCTOR(nsIdleServiceX) +NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIdleServiceX, nsIdleServiceX::GetInstance) #include "nsMenuBarX.h" NS_GENERIC_FACTORY_CONSTRUCTOR(nsNativeMenuServiceX) diff --git a/widget/gonk/nsIdleServiceGonk.cpp b/widget/gonk/nsIdleServiceGonk.cpp index 171071c1e53..e219831fa6f 100644 --- a/widget/gonk/nsIdleServiceGonk.cpp +++ b/widget/gonk/nsIdleServiceGonk.cpp @@ -8,7 +8,7 @@ #include "nsIdleServiceGonk.h" #include "nsIServiceManager.h" -NS_IMPL_ISUPPORTS2(nsIdleServiceGonk, nsIIdleService, nsIdleService) +NS_IMPL_ISUPPORTS_INHERITED0(nsIdleServiceGonk, nsIdleService) bool nsIdleServiceGonk::PollIdleTime(PRUint32 *aIdleTime) diff --git a/widget/gonk/nsIdleServiceGonk.h b/widget/gonk/nsIdleServiceGonk.h index 4383089862e..88ff1f2db58 100644 --- a/widget/gonk/nsIdleServiceGonk.h +++ b/widget/gonk/nsIdleServiceGonk.h @@ -13,10 +13,25 @@ class nsIdleServiceGonk : public nsIdleService { public: - NS_DECL_ISUPPORTS + NS_DECL_ISUPPORTS_INHERITED bool PollIdleTime(PRUint32* aIdleTime); + + static already_AddRefed GetInstance() + { + nsIdleServiceGonk* idleService = + static_cast(nsIdleService::GetInstance().get()); + if (!idleService) { + idleService = new nsIdleServiceGonk(); + NS_ADDREF(idleService); + } + + return idleService; + } + protected: + nsIdleServiceGonk() { } + virtual ~nsIdleServiceGonk() { } bool UsePollMode(); }; diff --git a/widget/gonk/nsWidgetFactory.cpp b/widget/gonk/nsWidgetFactory.cpp index 46351fc193a..47765a6c600 100644 --- a/widget/gonk/nsWidgetFactory.cpp +++ b/widget/gonk/nsWidgetFactory.cpp @@ -29,7 +29,7 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsWindow) NS_GENERIC_FACTORY_CONSTRUCTOR(nsScreenManagerGonk) NS_GENERIC_FACTORY_CONSTRUCTOR(PuppetScreenManager) NS_GENERIC_FACTORY_CONSTRUCTOR(nsHTMLFormatConverter) -NS_GENERIC_FACTORY_CONSTRUCTOR(nsIdleServiceGonk) +NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIdleServiceGonk, nsIdleServiceGonk::GetInstance) NS_GENERIC_FACTORY_CONSTRUCTOR(nsTransferable) NS_DEFINE_NAMED_CID(NS_APPSHELL_CID); diff --git a/widget/gonk/nsWindow.cpp b/widget/gonk/nsWindow.cpp index 9f38cb15500..09b7fb752e8 100644 --- a/widget/gonk/nsWindow.cpp +++ b/widget/gonk/nsWindow.cpp @@ -559,7 +559,7 @@ nsWindow::UserActivity() } if (mIdleService) { - mIdleService->ResetIdleTimeOut(); + mIdleService->ResetIdleTimeOut(0); } } diff --git a/widget/gonk/nsWindow.h b/widget/gonk/nsWindow.h index 38b96e7a48f..886ca5ab262 100644 --- a/widget/gonk/nsWindow.h +++ b/widget/gonk/nsWindow.h @@ -6,11 +6,10 @@ #define nsWindow_h #include "nsBaseWidget.h" +#include "nsIIdleServiceInternal.h" extern nsIntRect gScreenBounds; -class nsIdleService; - namespace mozilla { namespace gl { class GLContext; @@ -100,7 +99,7 @@ protected: bool mVisible; nsIntRegion mDirtyRegion; InputContext mInputContext; - nsCOMPtr mIdleService; + nsCOMPtr mIdleService; void BringToTop(); diff --git a/widget/gtk2/nsIdleServiceGTK.cpp b/widget/gtk2/nsIdleServiceGTK.cpp index 0111f4c3c3f..deb24877fdb 100644 --- a/widget/gtk2/nsIdleServiceGTK.cpp +++ b/widget/gtk2/nsIdleServiceGTK.cpp @@ -29,7 +29,7 @@ static _XScreenSaverQueryExtension_fn _XSSQueryExtension = nsnull; static _XScreenSaverAllocInfo_fn _XSSAllocInfo = nsnull; static _XScreenSaverQueryInfo_fn _XSSQueryInfo = nsnull; -NS_IMPL_ISUPPORTS2(nsIdleServiceGTK, nsIdleService, nsIIdleService) +NS_IMPL_ISUPPORTS_INHERITED0(nsIdleServiceGTK, nsIdleService) static void Initialize() { diff --git a/widget/gtk2/nsIdleServiceGTK.h b/widget/gtk2/nsIdleServiceGTK.h index a26e2063b82..e755f256450 100644 --- a/widget/gtk2/nsIdleServiceGTK.h +++ b/widget/gtk2/nsIdleServiceGTK.h @@ -25,16 +25,28 @@ typedef struct { class nsIdleServiceGTK : public nsIdleService { public: - NS_DECL_ISUPPORTS - nsIdleServiceGTK(); + NS_DECL_ISUPPORTS_INHERITED bool PollIdleTime(PRUint32* aIdleTime); + static already_AddRefed GetInstance() + { + nsIdleServiceGTK* idleService = + static_cast(nsIdleService::GetInstance().get()); + if (!idleService) { + idleService = new nsIdleServiceGTK(); + NS_ADDREF(idleService); + } + + return idleService; + } + private: ~nsIdleServiceGTK(); XScreenSaverInfo* mXssInfo; protected: + nsIdleServiceGTK(); bool UsePollMode(); }; diff --git a/widget/gtk2/nsWidgetFactory.cpp b/widget/gtk2/nsWidgetFactory.cpp index d8d6b7975d1..0a189a59a84 100644 --- a/widget/gtk2/nsWidgetFactory.cpp +++ b/widget/gtk2/nsWidgetFactory.cpp @@ -66,6 +66,7 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsTransferable) NS_GENERIC_FACTORY_CONSTRUCTOR(nsBidiKeyboard) NS_GENERIC_FACTORY_CONSTRUCTOR(nsHTMLFormatConverter) #ifdef MOZ_X11 +NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIdleServiceGTK, nsIdleServiceGTK::GetInstance) NS_GENERIC_FACTORY_CONSTRUCTOR(nsClipboardHelper) NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsClipboard, Init) NS_GENERIC_FACTORY_CONSTRUCTOR(nsDragService) @@ -109,7 +110,6 @@ nsNativeThemeGTKConstructor(nsISupports *aOuter, REFNSIID aIID, #endif #if defined(MOZ_X11) -NS_GENERIC_FACTORY_CONSTRUCTOR(nsIdleServiceGTK) namespace mozilla { namespace widget { // This constructor should really be shared with all platforms. diff --git a/widget/gtk2/nsWindow.cpp b/widget/gtk2/nsWindow.cpp index f90890f8943..e0e4b6d4dd1 100644 --- a/widget/gtk2/nsWindow.cpp +++ b/widget/gtk2/nsWindow.cpp @@ -69,7 +69,7 @@ #include "nsGfxCIID.h" #include "nsIObserverService.h" -#include "nsIdleService.h" +#include "nsIIdleServiceInternal.h" #include "nsIPropertyBag2.h" #ifdef ACCESSIBILITY @@ -322,10 +322,10 @@ static inline bool TimestampIsNewerThan(guint32 a, guint32 b) static void UpdateLastInputEventTime(void *aGdkEvent) { - nsCOMPtr idleService = + nsCOMPtr idleService = do_GetService("@mozilla.org/widget/idleservice;1"); if (idleService) { - idleService->ResetIdleTimeOut(); + idleService->ResetIdleTimeOut(0); } guint timestamp = gdk_event_get_time(static_cast(aGdkEvent)); diff --git a/widget/nsGUIEvent.h b/widget/nsGUIEvent.h index fa3b624107f..b131cf72d5e 100644 --- a/widget/nsGUIEvent.h +++ b/widget/nsGUIEvent.h @@ -218,6 +218,10 @@ class nsHashKey; // Indicates a resize will occur #define NS_BEFORERESIZE_EVENT (NS_WINDOW_START + 66) +// Indicates that the user is either idle or active +#define NS_MOZ_USER_IDLE (NS_WINDOW_START + 67) +#define NS_MOZ_USER_ACTIVE (NS_WINDOW_START + 68) + #define NS_MOUSE_MESSAGE_START 300 #define NS_MOUSE_MOVE (NS_MOUSE_MESSAGE_START) #define NS_MOUSE_BUTTON_UP (NS_MOUSE_MESSAGE_START + 1) diff --git a/widget/nsIIdleService.idl b/widget/nsIIdleService.idl index 7d4cf5146f1..bfcbf7fc20c 100644 --- a/widget/nsIIdleService.idl +++ b/widget/nsIIdleService.idl @@ -66,3 +66,13 @@ interface nsIIdleService : nsISupports void removeIdleObserver(in nsIObserver observer, in unsigned long time); }; +%{C++ + /** + * Observer topic notification for idle window: OBSERVER_TOPIC_IDLE. + * Observer topic notification for active window: OBSERVER_TOPIC_ACTIVE. + */ + + #define OBSERVER_TOPIC_IDLE "idle" + #define OBSERVER_TOPIC_ACTIVE "active" + #define OBSERVER_TOPIC_IDLE_DAILY "idle-daily" +%} diff --git a/widget/nsIIdleServiceInternal.idl b/widget/nsIIdleServiceInternal.idl new file mode 100644 index 00000000000..350766ec3b3 --- /dev/null +++ b/widget/nsIIdleServiceInternal.idl @@ -0,0 +1,18 @@ +/* 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 "nsIIdleService.idl" + +[scriptable, uuid(7b89a2e7-ed12-42e0-b86d-4984239abd7b)] +interface nsIIdleServiceInternal : nsIIdleService +{ + /** + * "Resets the idle time to the value specified." + * + * @param idleDelta the time (in milliseconds) since the last user inter + * action + **/ + void resetIdleTimeOut(in unsigned long idleDeltaInMS); +}; + diff --git a/widget/os2/nsIdleServiceOS2.cpp b/widget/os2/nsIdleServiceOS2.cpp index c18c46a0d61..ed1353d7c78 100644 --- a/widget/os2/nsIdleServiceOS2.cpp +++ b/widget/os2/nsIdleServiceOS2.cpp @@ -10,7 +10,7 @@ static int (*_System DSSaver_GetInactivityTime)(ULONG *, ULONG *); #define SSCORE_NOERROR 0 // as in the DSSaver header files -NS_IMPL_ISUPPORTS2(nsIdleServiceOS2, nsIIdleService, nsIdleService) +NS_IMPL_ISUPPORTS_INHERITED0(nsIdleServiceOS2, nsIdleService) nsIdleServiceOS2::nsIdleServiceOS2() : mHMod(NULLHANDLE), mInitialized(false) diff --git a/widget/os2/nsIdleServiceOS2.h b/widget/os2/nsIdleServiceOS2.h index 8cd8022269d..1a6b1bf084c 100644 --- a/widget/os2/nsIdleServiceOS2.h +++ b/widget/os2/nsIdleServiceOS2.h @@ -15,19 +15,30 @@ class nsIdleServiceOS2 : public nsIdleService { public: - NS_DECL_ISUPPORTS - - nsIdleServiceOS2(); - ~nsIdleServiceOS2(); + NS_DECL_ISUPPORTS_INHERITED // ask the DSSaver DLL (sscore.dll) for the time of the last activity bool PollIdleTime(PRUint32 *aIdleTime); + static already_AddRefed GetInstance() + { + nsIdleServiceOS2* idleService = + static_cast(nsIdleService::GetInstance().get()); + if (!idleService) { + idleService = new nsIdleServiceOS2(); + NS_ADDREF(idleService); + } + + return idleService; + } + private: HMODULE mHMod; // module handle for screensaver DLL bool mInitialized; // fully initialized (function found in screensaver DLL?) protected: + nsIdleServiceOS2(); + ~nsIdleServiceOS2(); bool UsePollMode(); }; diff --git a/widget/os2/nsWidgetFactory.cpp b/widget/os2/nsWidgetFactory.cpp index eb6a938a835..267df075eb2 100644 --- a/widget/os2/nsWidgetFactory.cpp +++ b/widget/os2/nsWidgetFactory.cpp @@ -70,7 +70,8 @@ NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrintOptionsOS2, Init) NS_GENERIC_FACTORY_CONSTRUCTOR(nsPrinterEnumeratorOS2) NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrintSession, Init) NS_GENERIC_FACTORY_CONSTRUCTOR(nsScreenManagerOS2) -NS_GENERIC_FACTORY_CONSTRUCTOR(nsIdleServiceOS2) +NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIdleServiceOS2, + nsIdleServiceOS2::GetInstance) // component definition, will be exported using XPCOM NS_DEFINE_NAMED_CID(NS_APPSHELL_CID); diff --git a/widget/qt/nsIdleServiceQt.cpp b/widget/qt/nsIdleServiceQt.cpp index 98cceb19a9f..f3d211bb1c4 100644 --- a/widget/qt/nsIdleServiceQt.cpp +++ b/widget/qt/nsIdleServiceQt.cpp @@ -29,7 +29,7 @@ static _XScreenSaverQueryInfo_fn _XSSQueryInfo = nsnull; static bool sInitialized = false; -NS_IMPL_ISUPPORTS2(nsIdleServiceQt, nsIIdleService, nsIdleService) +NS_IMPL_ISUPPORTS_INHERITED0(nsIdleServiceQt, nsIdleService) nsIdleServiceQt::nsIdleServiceQt() #if !defined(MOZ_PLATFORM_MAEMO) && defined(MOZ_X11) diff --git a/widget/qt/nsIdleServiceQt.h b/widget/qt/nsIdleServiceQt.h index e42e2c5ac12..9ed9642eeba 100644 --- a/widget/qt/nsIdleServiceQt.h +++ b/widget/qt/nsIdleServiceQt.h @@ -27,18 +27,30 @@ typedef struct { class nsIdleServiceQt : public nsIdleService { public: - NS_DECL_ISUPPORTS - nsIdleServiceQt(); + NS_DECL_ISUPPORTS_INHERITED bool PollIdleTime(PRUint32* aIdleTime); + static already_AddRefed GetInstance() + { + nsIdleServiceQt* idleService = + static_cast(nsIdleService::GetInstance().get()); + if (!idleService) { + idleService = new nsIdleServiceQt(); + NS_ADDREF(idleService); + } + + return idleService; + } + private: - ~nsIdleServiceQt(); #if !defined(MOZ_PLATFORM_MAEMO) && defined(MOZ_X11) XScreenSaverInfo* mXssInfo; #endif protected: + nsIdleServiceQt(); + virtual ~nsIdleServiceQt(); bool UsePollMode(); }; diff --git a/widget/qt/nsWidgetFactory.cpp b/widget/qt/nsWidgetFactory.cpp index 16cf4d8305f..a63ba36233e 100644 --- a/widget/qt/nsWidgetFactory.cpp +++ b/widget/qt/nsWidgetFactory.cpp @@ -50,7 +50,7 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsClipboard) NS_GENERIC_FACTORY_CONSTRUCTOR(nsClipboardHelper) NS_GENERIC_FACTORY_CONSTRUCTOR(nsDragService) NS_GENERIC_FACTORY_CONSTRUCTOR(nsBidiKeyboard) -NS_GENERIC_FACTORY_CONSTRUCTOR(nsIdleServiceQt) +NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIdleServiceQt, nsIdleServiceQt::GetInstance) NS_GENERIC_FACTORY_CONSTRUCTOR(nsSound) static nsresult diff --git a/widget/qt/nsWindow.cpp b/widget/qt/nsWindow.cpp index 10c85a4cb3c..04a3df1b854 100644 --- a/widget/qt/nsWindow.cpp +++ b/widget/qt/nsWindow.cpp @@ -3300,7 +3300,7 @@ nsWindow::UserActivity() } if (mIdleService) { - mIdleService->ResetIdleTimeOut(); + mIdleService->ResetIdleTimeOut(0); } } diff --git a/widget/qt/nsWindow.h b/widget/qt/nsWindow.h index b139112eb48..017e50740e8 100644 --- a/widget/qt/nsWindow.h +++ b/widget/qt/nsWindow.h @@ -21,6 +21,7 @@ #include "nsWeakReference.h" #include "nsGkAtoms.h" +#include "nsIIdleServiceInternal.h" #ifdef MOZ_LOGGING @@ -324,7 +325,7 @@ private: PluginType mPluginType; nsRefPtr mThebesSurface; - nsCOMPtr mIdleService; + nsCOMPtr mIdleService; bool mIsTransparent; diff --git a/widget/windows/nsIdleServiceWin.cpp b/widget/windows/nsIdleServiceWin.cpp index 589cd40ff35..3dac451a2b0 100644 --- a/widget/windows/nsIdleServiceWin.cpp +++ b/widget/windows/nsIdleServiceWin.cpp @@ -8,7 +8,7 @@ #include "nsIdleServiceWin.h" #include -NS_IMPL_ISUPPORTS2(nsIdleServiceWin, nsIIdleService, nsIdleService) +NS_IMPL_ISUPPORTS_INHERITED0(nsIdleServiceWin, nsIdleService) bool nsIdleServiceWin::PollIdleTime(PRUint32 *aIdleTime) diff --git a/widget/windows/nsIdleServiceWin.h b/widget/windows/nsIdleServiceWin.h index 7326b3ecab9..64087fe5920 100644 --- a/widget/windows/nsIdleServiceWin.h +++ b/widget/windows/nsIdleServiceWin.h @@ -22,11 +22,25 @@ class nsIdleServiceWin : public nsIdleService { public: - NS_DECL_ISUPPORTS + NS_DECL_ISUPPORTS_INHERITED bool PollIdleTime(PRUint32* aIdleTime); + static already_AddRefed GetInstance() + { + nsIdleServiceWin* idleService = + static_cast(nsIdleService::GetInstance().get()); + if (!idleService) { + idleService = new nsIdleServiceWin(); + NS_ADDREF(idleService); + } + + return idleService; + } + protected: + nsIdleServiceWin() { } + virtual ~nsIdleServiceWin() { } bool UsePollMode(); }; diff --git a/widget/windows/nsWidgetFactory.cpp b/widget/windows/nsWidgetFactory.cpp index 19c51324067..8c3906e5de3 100644 --- a/widget/windows/nsWidgetFactory.cpp +++ b/widget/windows/nsWidgetFactory.cpp @@ -49,7 +49,7 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsWindow) NS_GENERIC_FACTORY_CONSTRUCTOR(ChildWindow) NS_GENERIC_FACTORY_CONSTRUCTOR(nsFilePicker) NS_GENERIC_FACTORY_CONSTRUCTOR(nsScreenManagerWin) -NS_GENERIC_FACTORY_CONSTRUCTOR(nsIdleServiceWin) +NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIdleServiceWin, nsIdleServiceWin::GetInstance) NS_GENERIC_FACTORY_CONSTRUCTOR(nsClipboard) NS_GENERIC_FACTORY_CONSTRUCTOR(nsClipboardHelper) NS_GENERIC_FACTORY_CONSTRUCTOR(nsSound) diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index 9bc266fbfb4..cde60247cfa 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -6131,7 +6131,7 @@ void nsWindow::UserActivity() // Check that we now have the idle service. if (mIdleService) { - mIdleService->ResetIdleTimeOut(); + mIdleService->ResetIdleTimeOut(0); } } diff --git a/widget/windows/nsWindow.h b/widget/windows/nsWindow.h index 407d1abfad1..ae1951f74ca 100644 --- a/widget/windows/nsWindow.h +++ b/widget/windows/nsWindow.h @@ -41,6 +41,8 @@ #include "nsIDOMMouseEvent.h" +#include "nsIIdleServiceInternal.h" + /** * Forward class definitions */ @@ -514,7 +516,7 @@ protected: // Height of the caption plus border PRInt32 mCaptionHeight; - nsCOMPtr mIdleService; + nsCOMPtr mIdleService; // Hook Data Memebers for Dropdowns. sProcessHook Tells the // hook methods whether they should be processing the hook diff --git a/widget/xpwidgets/nsIdleService.cpp b/widget/xpwidgets/nsIdleService.cpp index edabceb5c6b..e869a637b52 100644 --- a/widget/xpwidgets/nsIdleService.cpp +++ b/widget/xpwidgets/nsIdleService.cpp @@ -23,10 +23,6 @@ using namespace mozilla; -// observer topics used: -#define OBSERVER_TOPIC_IDLE "idle" -#define OBSERVER_TOPIC_BACK "back" -#define OBSERVER_TOPIC_IDLE_DAILY "idle-daily" // interval in milliseconds between internal idle time requests. #define MIN_IDLE_POLL_INTERVAL_MSEC (5 * PR_MSEC_PER_SEC) /* 5 sec */ @@ -73,7 +69,7 @@ nsIdleServiceDaily::Observe(nsISupports *, mShutdownInProgress = true; } - if (mShutdownInProgress || strcmp(aTopic, OBSERVER_TOPIC_BACK) == 0) { + if (mShutdownInProgress || strcmp(aTopic, OBSERVER_TOPIC_ACTIVE) == 0) { return NS_OK; } MOZ_ASSERT(strcmp(aTopic, OBSERVER_TOPIC_IDLE) == 0); @@ -264,6 +260,17 @@ nsIdleServiceDaily::DailyCallback(nsITimer* aTimer, void* aClosure) //////////////////////////////////////////////////////////////////////////////// //// nsIdleService +namespace { +nsIdleService* gIdleService; +} + +already_AddRefed +nsIdleService::GetInstance() +{ + nsRefPtr instance(gIdleService); + return instance.forget(); +} + nsIdleService::nsIdleService() : mCurrentlySetToTimeoutAtInPR(0), mAnyObserverIdle(false), mDeltaToNextIdleSwitchInS(PR_UINT32_MAX), @@ -273,6 +280,8 @@ nsIdleService::nsIdleService() : mCurrentlySetToTimeoutAtInPR(0), if (sLog == NULL) sLog = PR_NewLogModule("idleService"); #endif + MOZ_ASSERT(!gIdleService); + gIdleService = this; mDailyIdle = new nsIdleServiceDaily(this); mDailyIdle->Init(); } @@ -282,8 +291,14 @@ nsIdleService::~nsIdleService() if(mTimer) { mTimer->Cancel(); } + + + MOZ_ASSERT(gIdleService == this); + gIdleService = nsnull; } +NS_IMPL_ISUPPORTS2(nsIdleService, nsIIdleService, nsIIdleServiceInternal) + NS_IMETHODIMP nsIdleService::AddIdleObserver(nsIObserver* aObserver, PRUint32 aIdleTimeInS) { @@ -373,7 +388,7 @@ nsIdleService::RemoveIdleObserver(nsIObserver* aObserver, PRUint32 aTimeInS) return NS_ERROR_FAILURE; } -void +NS_IMETHODIMP nsIdleService::ResetIdleTimeOut(PRUint32 idleDeltaInMS) { PR_LOG(sLog, PR_LOG_DEBUG, @@ -387,7 +402,7 @@ nsIdleService::ResetIdleTimeOut(PRUint32 idleDeltaInMS) if (!mAnyObserverIdle) { PR_LOG(sLog, PR_LOG_DEBUG, ("idleService: Reset idle timeout: no idle observers")); - return; + return NS_OK; } // Mark all idle services as non-idle, and calculate the next idle timeout. @@ -422,7 +437,7 @@ nsIdleService::ResetIdleTimeOut(PRUint32 idleDeltaInMS) // Bail if nothing to do. if (!numberOfPendingNotifications) { - return; + return NS_OK; } // Now send "back" events to all, if any should have timed out allready, then @@ -444,10 +459,10 @@ nsIdleService::ResetIdleTimeOut(PRUint32 idleDeltaInMS) notifyList[numberOfPendingNotifications]); #endif notifyList[numberOfPendingNotifications]->Observe(this, - OBSERVER_TOPIC_BACK, + OBSERVER_TOPIC_ACTIVE, timeStr.get()); } - + return NS_OK; } NS_IMETHODIMP diff --git a/widget/xpwidgets/nsIdleService.h b/widget/xpwidgets/nsIdleService.h index ada2575f727..9994758d9a5 100644 --- a/widget/xpwidgets/nsIdleService.h +++ b/widget/xpwidgets/nsIdleService.h @@ -8,7 +8,7 @@ #ifndef nsIdleService_h__ #define nsIdleService_h__ -#include "nsIIdleService.h" +#include "nsIIdleServiceInternal.h" #include "nsCOMPtr.h" #include "nsITimer.h" #include "nsTArray.h" @@ -91,26 +91,17 @@ private: bool mShutdownInProgress; }; -class nsIdleService : public nsIIdleService +class nsIdleService : public nsIIdleServiceInternal { public: - nsIdleService(); - - // Implement nsIIdleService methods. - NS_IMETHOD AddIdleObserver(nsIObserver* aObserver, PRUint32 aIdleTime); - NS_IMETHOD RemoveIdleObserver(nsIObserver* aObserver, PRUint32 aIdleTime); - NS_IMETHOD GetIdleTime(PRUint32* idleTime); - - /** - * Function that resets the idle time in the service, in other words it - * sets the time for the last user interaction to now, or now-idleDelta - * - * @param idleDelta the time (in milliseconds) since the last user inter - * action - **/ - void ResetIdleTimeOut(PRUint32 idleDeltaInMS = 0); + NS_DECL_ISUPPORTS + NS_DECL_NSIIDLESERVICE + NS_DECL_NSIIDLESERVICEINTERNAL protected: + static already_AddRefed GetInstance(); + + nsIdleService(); virtual ~nsIdleService(); /** diff --git a/xpcom/glue/nsTObserverArray.h b/xpcom/glue/nsTObserverArray.h index 475f22f690b..e6b61342a14 100644 --- a/xpcom/glue/nsTObserverArray.h +++ b/xpcom/glue/nsTObserverArray.h @@ -145,6 +145,25 @@ class nsAutoTObserverArray : protected nsTObserverArray_base { // // Mutation methods // + + // Insert a given element at the given index. + // @param index The index at which to insert item. + // @param item The item to insert, + // @return A pointer to the newly inserted element, or a null on DOM + template + elem_type *InsertElementAt(index_type aIndex, const Item& aItem) { + elem_type* item = mArray.InsertElementAt(aIndex, aItem); + AdjustIterators(aIndex, 1); + return item; + } + + // Same as above but without copy constructing. + // This is useful to avoid temporaries. + elem_type* InsertElementAt(index_type aIndex) { + elem_type* item = mArray.InsertElementAt(aIndex); + AdjustIterators(aIndex, 1); + return item; + } // Prepend an element to the array unless it already exists in the array. // 'operator==' must be defined for elem_type. @@ -152,7 +171,13 @@ class nsAutoTObserverArray : protected nsTObserverArray_base { // @return true if the element was found, or inserted successfully. template bool PrependElementUnlessExists(const Item& item) { - return Contains(item) || mArray.InsertElementAt(0, item) != nsnull; + if (Contains(item)) { + return true; + } + + bool inserted = mArray.InsertElementAt(0, item) != nsnull; + AdjustIterators(0, 1); + return inserted; } // Append an element to the array.