Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fixing bug 715041. Add support for Idle API. r=bent, jst

  • Loading branch information...
commit 7a26a02ee7157928a847abb522ba5e322a4ae0d1 1 parent e4d20a8
Bonnie Surender authored
Showing with 2,537 additions and 84 deletions.
  1. +19 −0 content/base/public/nsContentUtils.h
  2. +20 −0 content/base/src/nsContentUtils.cpp
  3. +38 −0 dom/base/Navigator.cpp
  4. +495 −5 dom/base/nsGlobalWindow.cpp
  5. +85 −3 dom/base/nsGlobalWindow.h
  6. +6 −3 dom/base/nsPIDOMWindow.h
  7. +8 −0 dom/base/test/Makefile.in
  8. +760 −0 dom/base/test/test_bug715041.xul
  9. +845 −0 dom/base/test/test_bug715041_removal.xul
  10. +1 −0  dom/interfaces/base/Makefile.in
  11. +13 −1 dom/interfaces/base/nsIDOMNavigator.idl
  12. +16 −0 dom/interfaces/base/nsIIdleObserver.idl
  13. +3 −0  modules/libpref/src/init/all.js
  14. +1 −0  widget/Makefile.in
  15. +1 −1  widget/android/nsIdleServiceAndroid.cpp
  16. +16 −1 widget/android/nsIdleServiceAndroid.h
  17. +1 −1  widget/android/nsWidgetFactory.cpp
  18. +1 −1  widget/android/nsWindow.cpp
  19. +2 −3 widget/android/nsWindow.h
  20. +15 −4 widget/cocoa/nsIdleServiceX.h
  21. +1 −1  widget/cocoa/nsWidgetFactory.mm
  22. +1 −1  widget/gonk/nsIdleServiceGonk.cpp
  23. +16 −1 widget/gonk/nsIdleServiceGonk.h
  24. +1 −1  widget/gonk/nsWidgetFactory.cpp
  25. +1 −1  widget/gonk/nsWindow.cpp
  26. +2 −3 widget/gonk/nsWindow.h
  27. +1 −1  widget/gtk2/nsIdleServiceGTK.cpp
  28. +14 −2 widget/gtk2/nsIdleServiceGTK.h
  29. +1 −1  widget/gtk2/nsWidgetFactory.cpp
  30. +3 −3 widget/gtk2/nsWindow.cpp
  31. +4 −0 widget/nsGUIEvent.h
  32. +10 −0 widget/nsIIdleService.idl
  33. +18 −0 widget/nsIIdleServiceInternal.idl
  34. +1 −1  widget/os2/nsIdleServiceOS2.cpp
  35. +15 −4 widget/os2/nsIdleServiceOS2.h
  36. +2 −1  widget/os2/nsWidgetFactory.cpp
  37. +1 −1  widget/qt/nsIdleServiceQt.cpp
  38. +15 −3 widget/qt/nsIdleServiceQt.h
  39. +1 −1  widget/qt/nsWidgetFactory.cpp
  40. +1 −1  widget/qt/nsWindow.cpp
  41. +2 −1  widget/qt/nsWindow.h
  42. +1 −1  widget/windows/nsIdleServiceWin.cpp
  43. +15 −1 widget/windows/nsIdleServiceWin.h
  44. +1 −1  widget/windows/nsWidgetFactory.cpp
  45. +1 −1  widget/windows/nsWindow.cpp
  46. +3 −1 widget/windows/nsWindow.h
  47. +25 −10 widget/xpwidgets/nsIdleService.cpp
  48. +8 −17 widget/xpwidgets/nsIdleService.h
  49. +26 −1 xpcom/glue/nsTObserverArray.h
19 content/base/public/nsContentUtils.h
View
@@ -1831,6 +1831,11 @@ class nsContentUtils
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
* in the same tab as this document receive key events outside of our
@@ -2013,6 +2018,19 @@ class nsContentUtils
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 @@ class nsContentUtils
static bool sIsFullScreenApiEnabled;
static bool sTrustedFullScreenOnly;
static PRUint32 sHandlingInputTimeout;
+ static bool sIsIdleObserverAPIEnabled;
static nsHtml5StringParser* sHTMLFragmentParser;
static nsIParser* sXMLFragmentParser;
20 content/base/src/nsContentUtils.cpp
View
@@ -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<nsIIdleService> 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.
38 dom/base/Navigator.cpp
View
@@ -37,6 +37,7 @@
#include "mozilla/ClearOnShutdown.h"
#include "Connection.h"
#include "MobileConnection.h"
+#include "nsIIdleObserver.h"
#ifdef MOZ_MEDIA_NAVIGATOR
#include "MediaManager.h"
@@ -651,6 +652,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<nsPIDOMWindow> 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<nsPIDOMWindow> 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)
{
nsCOMPtr<nsPIDOMWindow> win = do_QueryReferent(mWindow);
500 dom/base/nsGlobalWindow.cpp
View
@@ -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 <android/log.h>
@@ -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 @@ class nsGlobalWindowObserver MOZ_FINAL : public nsIObserver {
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<nsIScriptGlobalObject*>(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<nsIIdleObserver> mIdleObserver;
+ PRUint32 mTimeInS;
+ nsRefPtr<nsGlobalWindow> mIdleWindow;
+
+ // If false then call on active
+ bool mCallOnidle;
+};
+
+void
+nsGlobalWindow::NotifyIdleObserver(nsIIdleObserver* aIdleObserver,
+ PRUint32 aIdleObserverTimeInS,
+ bool aCallOnidle)
+{
+ nsCOMPtr<nsIRunnable> 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<IdleObserverHolder>::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<nsGlobalWindow> idleWindow = static_cast<nsGlobalWindow*>(aClosure);
+ MOZ_ASSERT(idleWindow, "Idle window has not been instantiated.");
+ idleWindow->NotifyIdleObserversOfIdleActiveEvent();
+}
+
+void
+IdleObserverTimerCallback(nsITimer* aTimer, void* aClosure)
+{
+ nsRefPtr<nsGlobalWindow> idleWindow = static_cast<nsGlobalWindow*>(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<PRUint32>(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<PRUint32>(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<nsGlobalWindow> 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<nsGlobalWindow> 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<IdleObserverHolder>::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<IdleObserverHolder>::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<PRInt32>(insertAtIndex) < mIdleCallbackIndex) {
+ NotifyIdleObserver(tmpIdleObserver.mIdleObserver,
+ tmpIdleObserver.mTimeInS,
+ true);
+ mIdleCallbackIndex++;
+ return NS_OK;
+ }
+
+ if (static_cast<PRInt32>(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<IdleObserverHolder>::ForwardIterator iter(mIdleObservers);
+ while (iter.HasMore()) {
+ IdleObserverHolder& idleObserver = iter.GetNext();
+ if (idleObserver.mTimeInS == aIdleObserverTimeInS &&
+ idleObserver.mIdleObserver == aIdleObserver ) {
+ break;
+ }
+ (*aRemoveElementIndex)++;
+ }
+ return static_cast<PRUint32>(*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<nsGlobalWindow> 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<PRUint32>(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<nsIDocShellTreeNode> node =
do_QueryInterface(GetDocShell());
if (node) {
88 dom/base/nsGlobalWindow.h
View
@@ -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 @@ struct nsTimeout : PRCList
nsAutoRefCnt mRefCnt;
};
+struct IdleObserverHolder
+{
+ nsCOMPtr<nsIIdleObserver> 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 @@ class nsGlobalWindow : public nsPIDOMWindow,
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<IdleObserverHolder> mIdleObservers;
+
+ // Idle timer used for function callbacks to notify idle observers.
+ nsCOMPtr<nsITimer> 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 <nsIIdleService> mIdleService;
+
+ static bool sIdleObserversAPIFuzzTimeDisabled;
+
friend class HashchangeCallback;
friend class nsBarProp;
@@ -696,6 +765,17 @@ class nsGlobalWindow : public nsPIDOMWindow,
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 @@ class nsGlobalWindow : public nsPIDOMWindow,
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 @@ class nsGlobalWindow : public nsPIDOMWindow,
static void NotifyDOMWindowFrozen(nsGlobalWindow* aWindow);
static void NotifyDOMWindowThawed(nsGlobalWindow* aWindow);
-
+
void ClearStatus();
virtual void UpdateParentTarget();
@@ -832,7 +912,7 @@ class nsGlobalWindow : public nsPIDOMWindow,
// 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 @@ class nsGlobalWindow : public nsPIDOMWindow,
// 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.
9 dom/base/nsPIDOMWindow.h
View
@@ -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 @@ class nsPIDOMWindow : public nsIDOMWindowInternal
mIsActive = aActive;
}
+ virtual nsresult RegisterIdleObserver(nsIIdleObserver* aIdleObserver) = 0;
+ virtual nsresult UnregisterIdleObserver(nsIIdleObserver* aIdleObserver) = 0;
+
bool IsActive()
{
NS_PRECONDITION(IsOuterWindow(),
8 dom/base/test/Makefile.in
View
@@ -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)
+
760 dom/base/test/test_bug715041.xul
View
@@ -0,0 +1,760 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=715041
+-->
+ <window title="Mozilla Bug 715041"
+xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=715041"
+target="_blank">Mozilla Bug 715041</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+
+ /** Mock Idle Service Test for Bug 715041 **/
+ SpecialPowers.setBoolPref("dom.idle-observers-api.fuzz_time.disabled", true);
+
+ //class mock javascript idle service
+ var idleServiceObj = {
+ observers: [],
+ windowObservers: [],
+ idleTimeInMS: 5000, //in milli seconds
+
+ // takes note of the idle observers added as the minimum idle observer
+ // with the idle service
+ timesAdded: [],
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Components.interfaces.nsISupports) ||
+ iid.equals(Components.interfaces.nsIFactory) ||
+ iid.equals(Components.interfaces.nsIIdleService)) {
+ return this;
+ }
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ },
+
+ createInstance: function(outer, iid) {
+ return this.QueryInterface(iid);
+ },
+
+ get idleTime() {
+ return this.idleTimeInMS; //in milli seconds
+ },
+
+ set idleTime(timeInMS) {
+ this.idleTimeInMS = timeInMS;
+ },
+
+ getWindowFromObserver: function(observer) {
+ try {
+ var interfaceRequestor = observer.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
+ var window = interfaceRequestor.getInterface(Components.interfaces.nsIDOMWindow);
+ return window;
+ }
+ catch (e) {}
+
+ return null;
+ },
+
+ testIdleBackService: function(observer, topic) {
+ dump("\nJS FAKE IDLE SERVICE\n");
+ dump("JS NUM OBSERVERS: " + this.observers.length + "\n");
+
+ if (this.observers.length > 1) {
+ this.observers[1].observer.observe(observer, topic, '\0');
+ dump("JS CALLED OBSERVE FUNCTION!!!\n\n");
+ }
+ },
+
+ addIdleObserver: function(observer, time) {
+ dump("\nJS FAKE IDLE SERVICE add idle observer before\n");
+ dump("JS NUM OBSERVERS: " + this.observers.length + "\n");
+
+ var window = this.getWindowFromObserver(observer);
+ dump("window is: " + window + "\n");
+
+ if (window) {
+ this.observers.push({ observer: observer, time: time, });
+ addedIdleObserver = true;
+ numIdleObserversAdded++;
+ this.timesAdded.push(time);
+
+ dump ("\nMOCK IDLE SERVICE ADDING idle observer with time: " + time + "\n");
+ dump("MOCK IDLE SERVICE: num idle observers added: " + numIdleObserversAdded + "\n\n");
+ }
+ else {
+ dump("SHOULD NEVER GET HERE!");
+ oldIdleService.addIdleObserver(observer, time);
+ addedIdleObserver = false;
+ }
+
+ dump("\nJS FAKE IDLE SERVICE end of add idle observer\n");
+ dump("JS NUM OBSERVERS: " + this.observers.length + "\n");
+ },
+
+ removeIdleObserver: function(observer, time) {
+ dump("\nJS REMOVE IDLE OBSERVER () time to be removed: " + time + "\n");
+ var window = this.getWindowFromObserver(observer);
+ if (!window) {
+ oldIdleService.removeIdleObserver(observer, time);
+ }
+ else {
+ var observerIndex = -1;
+ for (var i=0; i<this.observers.length; i++) {
+ dump("JS removeIdleObserver() observer time: " + this.observers[i].time + "\n");
+ if (this.observers[i].time === time) {
+ observerIndex = i;
+ break;
+ }
+ }
+
+ if (observerIndex != -1 && this.observers.length > 0) {
+ numIdleObserversRemoved++;
+ this.observers.splice(observerIndex, 1);
+ removedIdleObserver = true;
+ dump("MOCK IDLE SERVICE REMOVING idle observer with time " + time + "\n");
+ dump("MOCK IDLE SERVICE numIdleObserversRemoved: " + numIdleObserversRemoved + " numIdleObserversAdded: " + numIdleObserversAdded + "\n\n");
+ }
+ else {
+ removedIdleObserver = false;
+ }
+ }
+ dump("\nJS FAKE IDLE SERVICE end of remove idle observer\n");
+ dump("JS NUM OBSERVERS: " + this.observers.length + "\n");
+ },
+ };
+
+ /** Test for Bug 715041 **/
+ dump("\n\n\nJS STARTING TESTING FOR BUG 715041\n");
+
+ //bool variables
+ var addedIdleObserver = removedIdleObserver = passed = cleanUp = false;
+
+ //test case enabled
+ var AddOutOfOrderActiveEnabled = AddOutOfOrderIdleEnabled =
+ AddShiftLocalEnabled = AddNewLocalWhileAllIdleEnabled =
+ ShiftLocalTimerBackEnabled =
+ AddRemoveIdleObserverWithInvalidTimeEnabled = true;
+
+ //msgXCount
+ var msg0Count = msg1Count = msg2Count = msg3Count = msg4Count = msg5Count =
+ msg6Count = tcZero = currTestCaseNum = prevMsgNum =
+ numIdleObserversRemoved = numIdleObserversAdded = 0;
+
+ //test case number
+ var tcZero = 0;
+ var tcAddOutOfOrderActive = 1;
+ var tcAddOutOfOrderIdle = 2;
+ var tcAddShiftLocal = 3;
+ var tcAddNewLocalWhileAllIdle = 4;
+ var tcShiftLocalTimerBack = 5;
+ var tcAddRemoveIdleObserverWithInvalidTime = 6;
+
+ function ResetMsgCounts() {
+ msg0Count = msg1Count = msg2Count = msg3Count = msg4Count = msg5Count =
+ msg6Count = prevMsgNum = 0;
+ }
+
+ function ResetVars() {
+ msg0Count = msg1Count = msg2Count = msg3Count = msg4Count = msg5Count =
+ msg6Count = prevMsgNum = 0;
+
+ numIdleObserversAdded = numIdleObserversRemoved = 0;
+ currTestCaseNum = -1;
+ addedIdleObserver = removedIdleObserver = passed = cleanUp = false;
+ }
+
+ /*
+ * - function printMsgCounts()
+ */
+ function printMsgCounts() {
+ dump("\nmsg0Count: " + msg0Count +
+ "\nmsg1Count: " + msg1Count +
+ "\nmsg2Count: " + msg2Count +
+ "\nmsg3Count: " + msg3Count +
+ "\nmsg5Count: " + msg5Count +
+ "\nmsg6Count: " + msg6Count +
+ "\n"
+ );
+ }
+
+ function performNextTest() {
+ dump("\nfunction performNextTest()\ncurrTestCaseNum: " + currTestCaseNum +
+ "\ncleanUp: " + cleanUp +
+ "\npassed: " + passed +
+ "\nnumIdleObserversRemoved: " + numIdleObserversRemoved +
+ "\nnumIdleObservesAdded: " + numIdleObserversAdded + "\n");
+
+ switch (currTestCaseNum) {
+ case tcZero:
+ ok(passed, "Test case 0 failed clean up!");
+ caseZeroCleanUp();
+ break;
+ case tcAddShiftLocal:
+ if (cleanUp && numIdleObserversRemoved === 1) {
+ passed = true;
+ ok(passed, "Failed test case AddShiftLocalCleanUp()");
+ if (AddNewLocalWhileAllIdleEnabled) {
+ AddNewLocalWhileAllIdle();
+ }
+ else {
+ SimpleTest.finish();
+ }
+ }
+ break;
+ case tcAddNewLocalWhileAllIdle:
+ ok(passed, "Failed test case: AddNewLocalWhileAllIdle()");
+ AddNewLocalWhileAllIdleCleanUp();
+ break;
+ default:
+ //do nothing.
+ break;
+ }
+ }
+
+ //Place Holder.
+ var idleHandler0 = function() { dump("rmsg 0, should never be used!\n"); };
+
+ //idleHandler1
+ function idleHandler1() {
+ msg1Count++;
+ dump("msg 1 Count: " + msg1Count + "\n");
+
+ switch (currTestCaseNum) {
+ case tcAddOutOfOrderIdle:
+ if (msg1Count === 1 && msg2Count === 1 && msg3Count === 1) {
+ idleServiceObj.idleTime = 0;
+ idleServiceObj.testIdleBackService(idleObserversArray[1], "active");
+ }
+ else if (msg1Count === 4 && msg2Count === 4 && msg3Count === 4) {
+ passed = true;
+ AddOutOfOrderIdleCleanUp();
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ //idleHandler2
+ function idleHandler2() {
+ msg2Count++;
+ dump("msg 2 Count: " + msg2Count + "\n");
+
+ switch (currTestCaseNum) {
+ case tcZero:
+ switch (msg2Count) {
+ case 2:
+ passed = true;
+ performNextTest();
+ break;
+ default:
+ break;
+ }
+ break;
+ case tcAddOutOfOrderIdle:
+ if (msg3Count === 1 && msg2Count === 1 && !msg1Count) {
+ idleServiceObj.idleTime = 4000;
+ window.navigator.addIdleObserver(idleObserversArray[1]);
+ }
+ break;
+ case tcAddShiftLocal:
+ if (!msg1Count && msg2Count === 1 && !msg3Count && !msg4Count) {
+ window.navigator.addIdleObserver(idleObserversArray[3]);
+ }
+ AddShiftLocalCleanUp();
+ break;
+ case tcAddNewLocalWhileAllIdle:
+ if (msg1Count === 1 && msg2Count === 2) {
+ idleServiceObj.idleTime = 3500;
+ window.navigator.addIdleObserver(idleObserversArray[5]);
+ }
+ break;
+ case (tcShiftLocalTimerBack):
+ if (!msg1Count && msg2Count === 1 && !msg3Count && !msg4Count && !msg5Count) {
+ window.navigator.addIdleObserver(idleObserversArray[5]);
+ window.navigator.addIdleObserver(idleObserversArray[4]);
+ }
+ break;
+ default:
+ //do nothing.
+ break;
+ }
+ }
+
+ //idleHandler3
+ function idleHandler3() {
+ msg3Count++;
+ dump("msg 3 Count: " + msg3Count + "\n");
+
+ switch (currTestCaseNum) {
+ case (tcAddOutOfOrderIdle):
+ if (msg3Count === 1) {
+ idleServiceObj.idleTime = 3500;
+ window.navigator.addIdleObserver(idleObserversArray[2]);
+ }
+ if (msg1Count === 2 && msg2Count === 2 && msg3Count === 2) {
+ idleServiceObj.idleTime = 1000;
+ idleServiceObj.testIdleBackService(idleObserversArray[1], "idle");
+ }
+ else if (msg1Count === 3 && msg2Count === 3 && msg3Count === 3) {
+ AddOutOfOrderIdle();
+ }
+ else if (msg1Count === 3 && msg2Count === 3 && msg3Count === 4) {
+ passed = true;
+ AddOutOfOrderIdleCleanUp();
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ //idleHandler4
+ function idleHandler4() {
+ msg4Count++;
+ dump("msg 4 Count: " + msg4Count + "\n");
+
+ switch (currTestCaseNum) {
+ case tcAddOutOfOrderActive:
+ if (msg1Count && msg2Count && msg3Count && msg4Count) {
+ passed = true;
+ ok(passed, "idleHandler4: failed AddOutOfOrderActive()");
+ AddOutOfOrderActiveCleanUp();
+ cleanUp = true;
+ }
+ break;
+ case tcAddShiftLocal:
+ if (msg1Count === 1 && msg3Count === 1 && msg4Count === 1) {
+ idleServiceObj.idleTime = 3200;
+ window.navigator.addIdleObserver(idleObserversArray[2]);
+ }
+ break;
+ default:
+ //do nothing.
+ break;
+ }
+ }
+
+ //idleHandler5
+ function idleHandler5() {
+ msg5Count++;
+ dump("msg 5 Count: " + msg5Count + "\n");
+
+ switch (currTestCaseNum) {
+ case tcAddNewLocalWhileAllIdle:
+ if (msg1Count === 1 && msg2Count === 2 && msg5Count === 1) {
+ passed = true;
+ performNextTest();
+ }
+ break;
+ case tcShiftLocalTimerBack:
+ if (!msg1Count && msg2Count === 1 && !msg3Count && msg4Count === 1 && msg5Count === 1) {
+ passed = true;
+ ShiftLocalTimerBackCleanUp();
+ }
+ break;
+ default:
+ //do nothing.
+ break;
+ }
+ }
+
+ //idleHandler6
+ function idleHandler6() {
+ dump("msg 6 Count: " + msg6Count + "\n");
+ }
+
+ var idleObserversArray = [];
+ idleObserversArray[0] = {time: 0, onidle: idleHandler0, onactive: idleHandler0};
+ idleObserversArray[1] = {time: 1, onidle: idleHandler1, onactive: idleHandler1};
+ idleObserversArray[2] = {time: 2, onidle: idleHandler2, onactive: idleHandler2};
+ idleObserversArray[3] = {time: 3, onidle: idleHandler3, onactive: idleHandler3};
+ idleObserversArray[4] = {time: 4, onidle: idleHandler4, onactive: idleHandler4};
+ idleObserversArray[5] = {time: 5, onidle: idleHandler5, onactive: idleHandler5};
+ idleObserversArray[6] = {time: 0, onidle: idleHandler6, onactive: idleHandler6};
+ idleObserversArray[7] = {time: 2, onidle: null, onactive: null};
+
+ idleServiceObj.observers.push( {observer: idleObserversArray[0], time: 0, } );
+
+ /*
+ * - case 0
+ * - AddSingleIdleObserver
+ * - takes care of adding duplicate local too
+ * - user is currently idle since the
+ * requested idle time of 2s < current idle time of 5000ms set below.
+ */
+ function caseZero() {
+ dump("\n\nTESTING CASE 0\n");
+ dump("==============\n");
+
+ ResetVars();
+ currTestCaseNum = tcZero;
+ idleServiceObj.idleTime = 5000;
+
+ window.navigator.addIdleObserver(idleObserversArray[2]);
+ idleServiceObj.testIdleBackService(idleObserversArray[2], "idle");
+ window.navigator.addIdleObserver(idleObserversArray[2]);
+ }
+
+ function caseZeroCleanUp() {
+ dump("\ncaseZeroCleanUp()\n");
+ dump("==============\n");
+ ResetVars();
+ currTestCaseNum = tcZero;
+ cleanUp = false;
+
+ window.navigator.removeIdleObserver(idleObserversArray[2]);
+ window.navigator.removeIdleObserver(idleObserversArray[2]);
+
+ if (AddOutOfOrderActiveEnabled) {
+ AddOutOfOrderActive();
+ }
+ else {
+ dump("Finishing testing idle API.\n");
+ SimpleTest.finish();
+ }
+ }
+
+ /*
+ AddOutOfOrderActive()
+ - Tests if the idle observer with the min time is always registered correctly
+ with the idle service.
+ */
+ function AddOutOfOrderActive() {
+ dump("\n\nTESTING CASE AddOutOfOrderActive\n");
+ dump("==============\n");
+
+ ResetVars();
+ currTestCaseNum = tcAddOutOfOrderActive;
+ idleServiceObj.idleTime = 500;
+
+ window.navigator.addIdleObserver(idleObserversArray[3]); //msg3
+ window.navigator.addIdleObserver(idleObserversArray[4]); //msg4
+ window.navigator.addIdleObserver(idleObserversArray[1]); //msg1
+ window.navigator.addIdleObserver(idleObserversArray[2]); //msg2
+ passed = false;
+
+ idleServiceObj.idleTime = 1000;
+ idleServiceObj.testIdleBackService(idleObserversArray[1], "idle");
+ }
+
+ /*
+ - AddOutOfOrderActiveCleanUp()
+ */
+ function AddOutOfOrderActiveCleanUp() {
+ dump("\nAddOutOfOrderActiveCleanUp()\n");
+ dump("==============================\n");
+ ResetVars();
+ currTestCaseNum = tcAddOutOfOrderActive;
+ cleanUp = false;
+ idleServiceObj.idleTime = 4500;
+
+ for (var i=1; i<5; i++) {
+ window.navigator.removeIdleObserver(idleObserversArray[i]);
+ }
+ dump("JS AddOutOfOrderActiveCleanUp() DONE\n");
+ if (AddOutOfOrderIdleEnabled) {
+ AddOutOfOrderIdle();
+ }
+ else {
+ dump("Finishing testing idle API.\n");
+ SimpleTest.finish();
+ }
+ }
+
+ /*
+ - AddOutOfOrderIdle()
+ */
+ function AddOutOfOrderIdle() {
+ dump("\nAddOutOfOrderIdle()\n");
+ dump("======================================================================\n");
+
+ dump("\nJS AddOutOfOrderIdle\n");
+ dump("JS NUM OBSERVERS: " + idleServiceObj.observers.length + "\n");
+
+ if (!msg1Count && !msg2Count && !msg3Count) {
+ ResetVars();
+ }
+ currTestCaseNum = tcAddOutOfOrderIdle;
+ cleanUp = false;
+
+ if (!msg1Count && !msg2Count && !msg3Count) {
+ idleServiceObj.idleTime = 3100;
+ }
+ window.navigator.addIdleObserver(idleObserversArray[3]);
+ if (!msg1Count && !msg2Count && !msg3Count) {
+ idleServiceObj.testIdleBackService(idleObserversArray[3], "idle");
+ }
+ }
+
+ /*
+ - AddOutOfOrderIdleCleanUp()
+ */
+ function AddOutOfOrderIdleCleanUp() {
+ ok(passed, "Failed test case: AddOutOfOrderIdle()");
+ dump("\nAddOutOfOrderIdleCleanUp()\n");
+ dump("==========================\n");
+ ResetVars();
+ currTestCaseNum = tcAddOutOfOrderIdle;
+ cleanUp = true;
+ idleServiceObj.idleTime = 4100;
+
+ for (var j=1; j<4; j++) {
+ window.navigator.removeIdleObserver(idleObserversArray[j]);
+ }
+ window.navigator.removeIdleObserver(idleObserversArray[3]);
+
+ if (idleServiceObj.observers.length === 1) {
+ passed = true;
+ }
+ else {
+ passed = false;
+ }
+ ok(passed, "Failed test case: AddOutOfOrderIdleCleanUp()");
+ if (AddShiftLocalEnabled) {
+ AddShiftLocal();
+ }
+ else {
+ dump("Finished AddOutOfOrderIdleCleanUp() test.\n");
+ SimpleTest.finish();
+ }
+ }
+
+ /*
+ * function AddShiftLocal()
+ * - user is idle
+ * - check that local idle timer is shifted correctly
+ * - msg 1 fired when user is idle
+ * - msg 3 fired when 3000
+ * - msg 2 fired immediately when added at 3200 ms
+ * - msg 4 fired by local timer.
+ */
+ function AddShiftLocal()
+ {
+ dump("\n\nTESTING CASE AddShiftLocal\n");
+ dump("==============\n");
+
+ ResetVars();
+ currTestCaseNum = tcAddShiftLocal;
+ idleServiceObj.idleTime = 500;
+
+ window.navigator.addIdleObserver(idleObserversArray[1]);
+ window.navigator.addIdleObserver(idleObserversArray[3]);
+ window.navigator.addIdleObserver(idleObserversArray[4]);
+
+ idleServiceObj.idleTime = 1000;
+ idleServiceObj.testIdleBackService(idleObserversArray[1], "idle");
+ }
+
+ /*
+ * function AddShiftLocalCleanUp()
+ */
+ function AddShiftLocalCleanUp()
+ {
+ dump("\n\nTESTING CASE AddShiftLocalCleanUp\n");
+ dump("==============\n");
+
+ ResetVars();
+ currTestCaseNum = tcAddShiftLocal;
+
+ for (var i=1; i<5; i++) {
+ window.navigator.removeIdleObserver(idleObserversArray[i]);
+ }
+ dump("AddShiftLocalCleanUp() done clean up\n");
+ if (AddNewLocalWhileAllIdleEnabled) {
+ AddNewLocalWhileAllIdle();
+ }
+ else {
+ dump("Finished testing AddShiftLocal()\n");
+ SimpleTest.finish();
+ }
+ }
+
+ /*
+ * AddNewLocalWhileAllIdle()
+ * - no local idle timer exists because all of the idle observers that were added had a requested
+ * idle time of < curr user idle time and so were fired immediately. No local timer was required.
+ * - now add an idle observer whose requested idle time is > current use idle time and > min idle
+ * requested time in the list of idle observers.
+ */
+ function AddNewLocalWhileAllIdle()
+ {
+ dump("\n\nTESTING CASE AddNewLocalWhileAllIdle\n");
+ dump("==============\n");
+
+ ResetVars();
+ currTestCaseNum = tcAddNewLocalWhileAllIdle;
+ idleServiceObj.idleTime = 500;
+
+ window.navigator.addIdleObserver(idleObserversArray[1]);
+ window.navigator.addIdleObserver(idleObserversArray[2]);
+ window.navigator.addIdleObserver(idleObserversArray[2]);
+
+ idleServiceObj.idleTime = 1000;
+ idleServiceObj.testIdleBackService(idleObserversArray[1], "idle");
+ }
+
+ function AddNewLocalWhileAllIdleCleanUp()
+ {
+ dump("\n\nTESTING CASE AddNewLocalWhileAllIdleCleanUp\n");
+ dump("==============\n");
+
+ ResetVars();
+ currTestCaseNum = tcAddNewLocalWhileAllIdle;
+
+ window.navigator.removeIdleObserver(idleObserversArray[1]);
+ window.navigator.removeIdleObserver(idleObserversArray[2]);
+ window.navigator.removeIdleObserver(idleObserversArray[2]);
+ window.navigator.removeIdleObserver(idleObserversArray[5]);
+
+ if (ShiftLocalTimerBackEnabled) {
+ ShiftLocalTimerBack();
+ }
+ else {
+ dump("Finished testing AddNewLocalWhileAllIdle()\n");
+ SimpleTest.finish();
+ }
+ }
+
+ /*
+ * ShiftLocalTimerBack()
+ * - add a new idle observer whose requested time is > current user idle time
+ * but < the current local timer that has been set.
+ * - the local timer will need to be reset to fire the new msg added.
+ * RESULT
+ * - should print all of them in order
+ */
+ function ShiftLocalTimerBack()
+ {
+ dump("\n\nTESTING CASE ShiftLocalTimerBack()\n");
+ dump("==============\n");
+
+ ResetVars();
+ currTestCaseNum = tcShiftLocalTimerBack;
+ idleServiceObj.idleTime = 2100;
+
+ window.navigator.addIdleObserver(idleObserversArray[2]);
+ idleServiceObj.testIdleBackService(idleObserversArray[2], "idle");
+ }
+
+ function ShiftLocalTimerBackCleanUp()
+ {
+ dump("\n\nTESTING CASE ShiftLocalTimerBackCleanUp\n");
+ dump("==============\n");
+
+ ResetVars();
+ currTestCaseNum = tcShiftLocalTimerBack;
+ window.navigator.removeIdleObserver(idleObserversArray[2]);
+ window.navigator.removeIdleObserver(idleObserversArray[4]);
+ window.navigator.removeIdleObserver(idleObserversArray[5]);
+ dump("ShiftLocalTimerBackCleanUp() done clean up\n");
+
+ try {
+ componentMgr.unregisterFactory(idleServiceCID, idleServiceObj);
+ }
+ catch(err) {
+ dump("test_bug715041.xul: ShiftLocalTimerBackCleanUp() Failed to unregister factory, mock idle service!\n");
+ }
+
+ try {
+ componentMgr.registerFactory(oldIdleServiceCID, "Re registering old idle service", idleServiceContractID, oldIdleServiceFactoryObj);
+ }
+ catch(err) {
+ dump("test_bug715041.xul: ShiftLocalTimerBackCleanUp() Failed to register factory, original idle service!\n");
+ }
+
+ SimpleTest.finish();
+ }
+
+ /*
+ * function AddRemoveIdleObserverWithInvalidTime()
+ */
+ function AddRemoveIdleObserverWithInvalidTime()
+ {
+ dump("\n\nTESTING CASE AddRemoveIdleObserverWithInvalidTime()\n");
+ dump("==============\n");
+
+ ResetVars();
+ currTestCaseNum = tcAddRemoveIdleObserverWithInvalidTime;
+
+ //while idle
+ idleServiceObj.idleTime = 2100;
+ var rv = window.navigator.addIdleObserver(idleObserversArray[6]);
+ dump("rv: " + rv + "\n");
+ rv = window.navigator.removeIdleObserver(idleObserversArray[6]);
+
+ idleServiceObj.idleTime = 0;
+ window.navigator.addIdleObserver(idleObserversArray[6]);
+ window.navigator.removeIdleObserver(idleObserversArray[6]);
+
+ SimpleTest.finish();
+ }
+
+ try {
+ var idleServiceCID = Components.ID("287075a6-f968-4516-8043-406c46f503b4");
+ var idleServiceContractID = "@mozilla.org/widget/idleservice;1";
+ var oldIdleService = Components.classes[idleServiceContractID].getService(Components.interfaces.nsIIdleService);
+ }
+ catch(ex) {
+ dump("test_bug715041.xul: 1) Failed to get old idle service.\n");
+ }
+
+ try {
+ // Registering new moch JS idle service
+ var componentMgr = Components.manager.QueryInterface(Components.interfaces.nsIComponentRegistrar);
+ }
+ catch(err) {
+ dump("test_bug715041.xul: Failed to query component registrar interface.\n");
+ }
+
+ try {
+ var oldIdleServiceFactoryObj = componentMgr.getClassObjectByContractID(idleServiceContractID, Components.interfaces.nsIFactory);
+ }
+ catch(err) {
+ dump("test_bug715041.xul: Failed to get old idle service.\n");
+ }
+
+ try {
+ var oldIdleServiceCID = componentMgr.contractIDToCID(idleServiceContractID);
+ }
+ catch(err) {
+ dump("test_bug715041.xul: Failed to convert ID to CID for old idle service.\n");
+ }
+
+ try {
+ componentMgr.unregisterFactory(oldIdleServiceCID, oldIdleServiceFactoryObj);
+ }
+ catch(err) {
+ dump("test_bug715041.xul: Failed to unregister old idle service factory object!\n");
+ }
+
+ try {
+ componentMgr.registerFactory(idleServiceCID, "Test Simple Idle/Back Notifications", idleServiceContractID, idleServiceObj);
+ }
+ catch(err) {
+ dump("test_bug715041.xul: Failed to register mock idle service.\n");
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.requestLongerTimeout(10);
+
+ AddOutOfOrderActiveEnabled = true;
+ AddOutOfOrderIdleEnabled = true;
+ AddNewLocalWhileAllIdleEnabled = true;
+ AddShiftLocalEnabled = true;
+ AddIdleObserverWithInvalidTimeEnabled = false;
+
+ caseZero();
+
+ ]]>
+ </script>
+ </window>
+
845 dom/base/test/test_bug715041_removal.xul
View
@@ -0,0 +1,845 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=715041
+-->
+ <window title="Mozilla Bug 715041"
+xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=715041"
+target="_blank">Mozilla Bug 715041</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+
+ /** Mock Idle Service Test for Bug 715041 **/
+ SpecialPowers.setBoolPref("dom.idle-observers-api.fuzz_time.disabled", true);
+
+ try {
+ var idleServiceCID = Components.ID("6f95d965-4322-4829-8a3c-5dc8a4587f4d");
+ var idleServiceContractID = "@mozilla.org/widget/idleservice;1";
+ var oldIdleService = Components.classes[idleServiceContractID].getService(Components.interfaces.nsIIdleService);
+ }
+ catch (ex) {
+ dump("test_bug715041_removal.xul: failed to get old idle service 1.");
+ }
+
+ //class mock javascript idle service
+ var idleServiceObj = {
+ observers: [],
+ windowObservers: [],
+ idleTimeInMS: 5000, //in milli seconds
+
+ // takes note of the idle observers added as the minimum idle observer
+ //with the idle service
+ timesAdded: [],
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Components.interfaces.nsISupports) ||
+ iid.equals(Components.interfaces.nsIFactory) ||
+ iid.equals(Components.interfaces.nsIIdleService)) {
+ return this;
+ }
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ },
+
+ createInstance: function(outer, iid) {
+ return this.QueryInterface(iid);
+ },
+
+ get idleTime() {
+ return this.idleTimeInMS; //in milli seconds
+ },
+
+ set idleTime(timeInMS) {
+ this.idleTimeInMS = timeInMS;
+ },
+
+ getWindowFromObserver: function(observer) {
+ try {
+ var interfaceRequestor = observer.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
+ var window = interfaceRequestor.getInterface(Components.interfaces.nsIDOMWindow);
+ return window;
+ }
+ catch (e) {}
+
+ return null;
+ },
+
+ testIdleBackService: function(observer, topic) {
+ dump("\nJS FAKE IDLE SERVICE\n");
+ dump("JS NUM OBSERVERS: " + this.observers.length + "\n");
+
+ if (this.observers.length > 1) {
+ this.observers[1].observer.observe(observer, topic, '\0');
+ dump("JS CALLED OBSERVE FUNCTION!!!\n\n");
+ }
+ },
+
+ addIdleObserver: function(observer, time) {
+ dump("\nJS FAKE IDLE SERVICE add idle observer before\n");
+ dump("JS NUM OBSERVERS: " + this.observers.length + "\n");
+ var window = this.getWindowFromObserver(observer);
+
+ if (window) {
+ this.observers.push({ observer: observer, time: time, });
+ addedIdleObserver = true;
+ numIdleObserversAdded++;
+ this.timesAdded.push(time);
+ dump ("\nMOCK IDLE SERVICE ADDING idle observer with time: " + time + "\n");
+ dump("MOCK IDLE SERVICE: num idle observers added: " + numIdleObserversAdded + "\n\n");
+ }
+ else {
+ dump("SHOULD NEVER GET HERE!");
+ oldIdleService.addIdleObserver(observer, time);
+ addedIdleObserver = false;
+ }
+
+ dump("\nJS FAKE IDLE SERVICE end of add idle observer\n");
+ dump("JS NUM OBSERVERS: " + this.observers.length + "\n");
+ },
+
+ removeIdleObserver: function(observer, time) {
+ dump("\nJS REMOVE IDLE OBSERVER () time to be removed: " + time + "\n");
+ var window = this.getWindowFromObserver(observer);
+ if (!window) {
+ oldIdleService.removeIdleObserver(observer, time);
+ }
+ else {
+ var observerIndex = -1;
+ for (var i=0; i<this.observers.length; i++) {
+ dump("JS removeIdleObserver() observer time: " + this.observers[i].time + "\n");
+ if (this.observers[i].time === time) {
+ observerIndex = i;
+ break;
+ }
+ }
+
+ if (observerIndex != -1 && this.observers.length > 0) {
+ numIdleObserversRemoved++;
+ this.observers.splice(observerIndex, 1);
+ removedIdleObserver = true;
+ dump("MOCK IDLE SERVICE REMOVING idle observer with time " + time + "\n");
+ dump("MOCK IDLE SERVICE numIdleObserversRemoved: " + numIdleObserversRemoved + " numIdleObserversAdded: " + numIdleObserversAdded + "\n\n");
+ }
+ else {
+ removedIdleObserver = false;
+ }
+ }
+ dump("\nJS FAKE IDLE SERVICE end of remove idle observer\n");
+ dump("JS NUM OBSERVERS: " + this.observers.length + "\n");
+ },
+ };
+
+ /** Test for Bug 715041 **/
+ dump("\n\n\nJS STARTING TESTING FOR BUG 715041 REMOVAL\n");
+
+ //bool variables
+ var addedIdleObserver = removedIdleObserver = passed = cleanUp = false;
+
+ //msgXCount
+ var msg0Count = msg1Count = msg2Count = msg3Count = msg4Count = msg5Count =
+ msg6Count = tcZero = currTestCaseNum = prevMsgNum = 0;
+
+ //test case number
+ var tcRemoveHeadIdleObserverWhileActive = 0;
+ var tcRemoveLocalIdleObserverWhileIdle = 1;
+ var tcRemoveHeadIdleObserver = 2;
+ var tcRemoveLocalIdleTimerWhileIdle = 3;
+ var tcRemoveLocalIdleTimerLastElement = 4;
+ var tcRemoveHeadAfterLastLocalFired = 5;
+ var tcRemoveHeadIdleObserverWhileIdleCase1 = 6;
+ var tcRemoveLastAddLast = 7;
+
+ function ResetMsgCounts() {
+ msg0Count = msg1Count = msg2Count = msg3Count = msg4Count = msg5Count =
+ msg6Count = prevMsgNum = 0;
+ }
+
+ function ResetVars() {
+ msg0Count = msg1Count = msg2Count = msg3Count = msg4Count = msg5Count =
+ msg6Count = prevMsgNum = 0;
+
+ numIdleObserversAdded = numIdleObserversRemoved = 0;
+ currTestCaseNum = -1;
+ addedIdleObserver = removedIdleObserver = passed = cleanUp = false;
+ }
+
+ function printVariableValues()
+ {
+ dump("\nfunction printVariableValues()\ncurrTestCaseNum: " + currTestCaseNum +
+ "\ncleanUp: " + cleanUp +
+ "\npassed: " + passed +
+ "\nnumIdleObserversRemoved: " + numIdleObserversRemoved +
+ "\nnumIdleObservesAdded: " + numIdleObserversAdded +
+ "\nmsg1Count " + msg1Count +
+ "\nmsg2Count " + msg2Count +
+ "\nmsg3Count " + msg3Count +
+ "\nmsg4Count " + msg4Count +
+ "\nmsg5Count " + msg5Count +
+ "\n");
+ }
+
+ //Place Holder.
+ var idleHandler0 = function() { dump("msg 0, should never be used!\n"); };
+
+ //idleHandler1
+ function idleHandler1() {
+ msg1Count++;
+ dump("msg 1 Count: " + msg1Count + "\n");
+
+ switch (currTestCaseNum) {
+ case tcRemoveHeadIdleObserver:
+ if (msg1Count === 1 && !msg2Count && !msg3Count && !msg4Count && !msg5Count) {
+ window.navigator.removeIdleObserver(idleObserversArray[1]);
+ }
+ break;
+ case tcRemoveLocalIdleObserverWhileIdle:
+ if (msg1Count === 1 && !msg2Count && !msg3Count && !msg4Count) {
+ window.navigator.removeIdleObserver(idleObserversArray[2]);
+ }
+ break;
+ case tcRemoveLocalIdleTimerWhileIdle:
+ if (msg1Count === 1 && !msg2Count && !msg3Count && !msg4Count) {
+ idleServiceObj.idleTime = 2000;
+ window.navigator.removeIdleObserver(idleObserversArray[3]);
+ }
+ break;
+ case tcRemoveHeadIdleObserverWhileIdleCase1:
+ if (msg1Count === 1 && !msg2Count && !msg3Count && !msg4Count) {
+ for (var i=1; i<4; i++) {
+ window.navigator.addIdleObserver(idleObserversArray[i]);
+ }
+ window.navigator.addIdleObserver(idleObserversArray[2]);
+ window.navigator.addIdleObserver(idleObserversArray[3]);
+
+ idleServiceObj.idleTime = 1200;
+ window.navigator.removeIdleObserver(idleObserversArray[1]);
+ }
+ break;
+ case tcRemoveLocalIdleTimerLastElement:
+ if (msg1Count === 1 && !msg2Count && !msg3Count && !msg4Count && !msg5Count) {
+ idleServiceObj.idleTime = 1500;
+ window.navigator.removeIdleObserver(idleObserversArray[1]);
+ idleServiceObj.idleTime = 1700;
+ window.navigator.addIdleObserver(idleObserversArray[2]);
+ idleServiceObj.idleTime = 2000;
+ idleServiceObj.testIdleBackService(idleObserversArray[2], "idle");
+ }
+ break;
+ case tcRemoveHeadAfterLastLocalFired:
+ if (msg1Count === 1 && !msg2Count && !msg3Count && !msg4Count && !msg5Count) {
+ dump("idle handler 1: case tcRemoveHeadAfterLastLocalFired:\n");
+ window.navigator.addIdleObserver(idleObserversArray[2]);
+ window.navigator.addIdleObserver(idleObserversArray[3]);
+ window.navigator.addIdleObserver(idleObserversArray[4]);
+ }
+ break;
+ case tcRemoveLastAddLast:
+ window.navigator.addIdleObserver(idleObserversArray[2]);
+ window.navigator.addIdleObserver(idleObserversArray[3]);
+ break;
+ default:
+ break;
+ }
+ }
+
+ //idleHandler2
+ function idleHandler2() {
+ msg2Count++;
+ dump("msg 2 Count: " + msg2Count + "\n");
+
+ switch (currTestCaseNum) {
+ case tcRemoveLocalIdleTimerLastElement:
+ if (msg1Count === 1 && msg2Count === 1 && !msg3Count && !msg4Count) {
+ window.navigator.addIdleObserver(idleObserversArray[3]);
+ window.navigator.addIdleObserver(idleObserversArray[4]);
+ window.navigator.removeIdleObserver(idleObserversArray[3]);
+ }
+ break;
+ default:
+ //do nothing.
+ break;
+ }
+ }
+
+ //idleHandler3
+ function idleHandler3() {
+ msg3Count++;
+ dump("msg 3 Count: " + msg3Count + "\n");
+ passed = false;
+ switch (currTestCaseNum) {
+ case tcRemoveHeadIdleObserverWhileActive:
+ if (!msg1Count && msg2Count === 1 && msg3Count === 1) {
+ passed = true;
+ }
+ dump("idleHandler3: passed: " + passed + "\n");
+ RemoveHeadIdleObserverWhileActiveCleanUp();
+ break;
+ case tcRemoveHeadIdleObserverWhileIdleCase1:
+ if (msg3Count != 2 && msg3Count != 4) {
+ return;
+ }
+
+ if (msg1Count === 2 && msg2Count === 2 && msg3Count === 2 && !msg4Count) {
+ passed = true;
+ ok(passed, "Failed test case remove head idle observer while idle case 1, part 1.\n");
+ idleServiceObj.testIdleBackService(idleObserversArray[1], "active");
+ return;
+ }
+
+ if (msg1Count === 3 && msg2Count === 4 && msg3Count === 4 &&
+ !msg4Count && !msg5Count) {
+ passed = true;
+ }
+ RemoveHeadIdleObserverWhileIdleCase1CleanUp();
+ break;
+ case tcRemoveLastAddLast:
+ if (msg1Count === 1 && msg2Count === 1 && msg3Count === 1
+ && !msg4Count && !msg5Count) {
+ idleServiceObj.idleTime = 3200;
+ window.navigator.removeIdleObserver(idleObserversArray[3]);
+ idleServiceObj.idleTime = 3500;
+ window.navigator.addIdleObserver(idleObserversArray[4]);
+ return;
+ }
+ break;
+ case tcRemoveHeadAfterLastLocalFired:
+ if (msg3Count === 1) {
+ return;
+ }
+
+ if (msg1Count === 2 && msg2Count === 2 && msg3Count === 2 && msg4Count === 1) {
+ passed = true;
+ }
+ RemoveHeadAfterLastLocalFiredCleanUp();
+ break;
+ default:
+ break;
+ }
+ }
+
+ //idleHandler4
+ function idleHandler4() {
+ msg4Count++;
+ dump("msg 4 Count: " + msg4Count + "\n");
+
+ switch (currTestCaseNum) {
+ case tcRemoveLocalIdleObserverWhileIdle:
+ if (msg1Count === 1 && !msg2Count && msg3Count === 1 && msg4Count === 1) {
+ passed = true;
+ RemoveLocalIdleObserverWhileIdleCleanUp();
+ }
+ break;
+ case tcRemoveHeadIdleObserver:
+ printVariableValues();
+ if (msg1Count === 1 && msg2Count === 1 && msg3Count === 1 && msg4Count === 1) {
+ passed = true;
+ RemoveHeadIdleObserverCleanUp();
+ }
+ break;
+ case tcRemoveLocalIdleTimerWhileIdle:
+ if (msg1Count === 1 && !msg2Count && !msg3Count && msg4Count === 1) {
+ passed = true;
+ RemoveLocalIdleTimerWhileIdleCleanUp();
+ }
+ break
+ case tcRemoveLocalIdleTimerLastElement:
+ if (msg1Count === 1 && msg2Count === 1 && !msg3Count && msg4Count === 1) {
+ passed = true;
+ ok(passed, "Failed test case remove local idle timer last element.\n");
+ RemoveLocalIdleTimerLastElementCleanUp();
+ }
+ break;
+ case tcRemoveHeadAfterLastLocalFired:
+ if (msg1Count === 1 && msg2Count === 1 && msg3Count === 1 && msg4Count === 1) {
+ window.navigator.removeIdleObserver(idleObserversArray[4]);
+ passed = true;
+ ok(passed, "Failed remove head after last local fired.\n");
+ idleServiceObj.testIdleBackService(idleObserversArray[1], "active");
+ }
+ break;
+ case tcRemoveLastAddLast:
+ if (msg1Count === 1 && msg2Count === 1 && msg3Count === 1 && msg4Count == 1) {
+ idleServiceObj.idleTime = 4100;
+ passed = true;
+ RemoveLastAddLastCleanUp();
+ }
+ break;
+ default:
+ //do nothing.
+ break;
+ }
+ }
+
+ //idleHandler5
+ function idleHandler5() {
+ msg5Count++;
+ dump("msg 5 Count: " + msg5Count + "\n");
+
+ switch (currTestCaseNum) {
+
+ default:
+ //do nothing.
+ break;
+ }
+ }
+
+ //idleHandler6
+ function idleHandler6() {
+ dump("msg 6 Count: " + msg6Count + "\n");
+ }
+
+ var idleObserversArray = [];
+ idleObserversArray[0] = {time: 0, onidle: idleHandler0, onactive: idleHandler0};
+ idleObserversArray[1] = {time: 1, onidle: idleHandler1, onactive: idleHandler1};
+ idleObserversArray[2] = {time: 2, onidle: idleHandler2, onactive: idleHandler2};
+ idleObserversArray[3] = {time: 3, onidle: idleHandler3, onactive: idleHandler3};
+ idleObserversArray[4] = {time: 4, onidle: idleHandler4, onactive: idleHandler4};
+ idleObserversArray[5] = {time: 5, onidle: idleHandler5, onactive: idleHandler5};
+ idleObserversArray[6] = {time: 0, onidle: idleHandler6, onactive: idleHandler6};
+
+ //observers array space holder at index zero
+ idleServiceObj.observers.push( {observer: idleObserversArray[0], time: 0, } );
+
+ /*
+ * - function RemoveHeadIdleObserverWhileActive1()
+ * - Remove head idle observer before the head idle notification is fired by the
+ * idle service. I.e. remove the head idle observer while the user is active.
+ * - RESULT: prints 2 in 2ms, 3
+ */
+ function RemoveHeadIdleObserverWhileActive() {
+ dump("\n\nTESTING CASE RemoveHeadIdleObserverWhileActive\n");
+ dump("=================================\n");
+
+ ResetVars();
+ currTestCaseNum = tcRemoveHeadIdleObserverWhileActive;
+ idleServiceObj.idleTime = 500;
+
+ window.navigator.addIdleObserver(idleObserversArray[1]);
+ dump("test_bug715041_removal.xul: RemoveHeadIdleObserverWhileActive() idle time " + idleServiceObj.idleTime + "\n");
+ window.navigator.addIdleObserver(idleObserversArray[2]);
+ window.navigator.addIdleObserver(idleObserversArray[3]);
+
+ idleServiceObj.idleTime = 800;
+ window.navigator.removeIdleObserver(idleObserversArray[1]);
+
+ idleServiceObj.idleTime = 1000;
+ idleServiceObj.testIdleBackService(idleObserversArray[2], "idle");
+ }
+
+ function RemoveHeadIdleObserverWhileActiveCleanUp() {
+ dump("\nRemoveHeadIdleObserverWhileActiveCleanUp()\n");
+ dump("=====================================\n");
+
+ dump("Passed: " + passed + "\n");
+ ok(passed, "Failed test case: RemoveHeadIdleObserverWhileActive");
+
+ ResetVars();
+ currTestCaseNum = tcRemoveHeadIdleObserverWhileActive;
+ idleServiceObj.idleTime = 3500;
+
+ for (var i=2; i<4; i++) {
+ window.navigator.removeIdleObserver(idleObserversArray[i]);
+ }
+
+ dump("JS RemoveHeadIdleObserverWhileActiveCleanUp() DONE\n");
+ if (RemoveLocalIdleObserverWhileIdleEnabled) {
+ RemoveLocalIdleObserverWhileIdle();
+ }
+ else {
+ dump("Finishing testing idle API.\n");
+ SimpleTest.finish();
+ }
+ }
+
+ /*
+ * function RemoveLocalIdleObserverWhileIdle()
+ * Remove local observer before the local oberver at index 1 is triggered while
+ * the user is idle.
+ * RESULT: should print 1, 3, 4
+ */
+ function RemoveLocalIdleObserverWhileIdle() {
+ dump("\n\nTESTING CASE RemoveLocalIdleObserverWhileIdle\n");
+ dump("=================================\n");
+
+ ResetVars();
+ currTestCaseNum = tcRemoveLocalIdleObserverWhileIdle;
+ idleServiceObj.idleTime = 500;
+
+ window.navigator.addIdleObserver(idleObserversArray[1]);
+ window.navigator.addIdleObserver(idleObserversArray[2]);
+ window.navigator.addIdleObserver(idleObserversArray[3]);
+ window.navigator.addIdleObserver(idleObserversArray[4]);
+
+ idleServiceObj.idleTime = 1000;
+ idleServiceObj.testIdleBackService(idleObserversArray[1], "idle");
+ }
+
+ function RemoveLocalIdleObserverWhileIdleCleanUp() {
+ dump("\nRemoveLocalIdleObserverWhileIdleCleanUp()\n");
+ dump("=====================================\n");
+
+ ok(passed, "Failed test case: RemoveLocalIdleObserverWhileIdleCleanUp()");
+
+ ResetVars();
+ currTestCaseNum = tcRemoveHeadIdleObserverWhileActive;
+ idleServiceObj.idleTime = 3500;
+
+ window.navigator.removeIdleObserver(idleObserversArray[1]);
+ window.navigator.removeIdleObserver(idleObserversArray[3]);
+ window.navigator.removeIdleObserver(idleObserversArray[4]);
+
+ dump("JS RemoveLocalIdleObserverWhileIdleCleanUp() DONE\n");
+ if (RemoveHeadIdleObserverEnabled) {
+ RemoveHeadIdleObserver();
+ }
+ else {
+ dump("Finishing testing idle API.\n");
+ SimpleTest.finish();
+ }
+ }
+
+
+ /*
+ * function RemoveHeadIdleObserver()
+ * Remove head idle observer while the user has been idle for 2400 ms.
+ * - RESULT: prints 1, 2, remove 2, 3, 4
+ */
+ function RemoveHeadIdleObserver() {
+ dump("\n\nTESTING CASE RemoveHeadIdleObserver\n");
+ dump("=================================\n");
+
+ ResetVars();
+ currTestCaseNum = tcRemoveHeadIdleObserver;
+ idleServiceObj.idleTime = 500;
+
+ window.navigator.addIdleObserver(idleObserversArray[1]);
+ window.navigator.addIdleObserver(idleObserversArray[2]);
+ window.navigator.addIdleObserver(idleObserversArray[3]);
+ window.navigator.addIdleObserver(idleObserversArray[4]);
+
+ idleServiceObj.idleTime = 1000;
+ idleServiceObj.testIdleBackService(idleObserversArray[1], "idle");
+ }
+
+ function RemoveHeadIdleObserverCleanUp() {
+ dump("\nRemoveHeadIdleObserverCleanUp()\n");
+ dump("=====================================\n");
+
+ ok(passed, "Failed test case: RemoveHeadIdleObserverCleanUp()");
+
+ ResetVars();
+ currTestCaseNum = tcRemoveHeadIdleObserver;
+ idleServiceObj.idleTime = 3500;
+
+ for (var i=2; i<5; i++) {
+ window.navigator.removeIdleObserver(idleObserversArray[i]);
+ }
+
+ dump("JS RemoveHeadIdleObserverCleanUp() DONE\n");
+ if (RemoveLocalIdleTimerWhileIdleEnabled) {
+ RemoveLocalIdleTimerWhileIdle();
+ }
+ else {
+ dump("Finishing testing idle API.\n");
+ SimpleTest.finish();
+ }
+ }
+
+ /*
+ * RemoveLocalIdleTimerWhileIdle()
+ * - Removes the idle observer that is also set as the current local idle timer callback
+ * local idle observer being removed is NOT at index 1!
+ * - RESULT: should trigger 1 in 1ms and 4 in 4ms
+ */
+ function RemoveLocalIdleTimerWhileIdle()
+ {
+ dump("\n\nTESTING CASE RemoveLocalIdleTimerWhileIdle\n");
+ dump("=================================\n");
+
+ ResetVars();
+ currTestCaseNum = tcRemoveLocalIdleTimerWhileIdle;
+ idleServiceObj.idleTime = 500;
+
+ window.navigator.addIdleObserver(idleObserversArray[1]);
+ window.navigator.addIdleObserver(idleObserversArray[3]);
+ window.navigator.addIdleObserver(idleObserversArray[4]);
+
+ idleServiceObj.idleTime = 1000;
+ idleServiceObj.testIdleBackService(idleObserversArray[1], "idle");
+ }
+
+ function RemoveLocalIdleTimerWhileIdleCleanUp()
+ {
+ dump("\nRemoveLocalIdleTimerWhileIdleCleanUp()\n");
+ dump("=====================================\n");
+
+ ok(passed, "Failed test case: RemoveLocalIdleTimerWhileIdleCleanUp()");
+
+ ResetVars();
+ currTestCaseNum = tcRemoveLocalIdleTimerWhileIdle;
+
+ window.navigator.removeIdleObserver(idleObserversArray[1]);
+ window.navigator.removeIdleObserver(idleObserversArray[4]);
+
+ dump("JS RemoveLocalIdleTimerWhileIdleCleanUp() DONE\n");
+ if (RemoveLocalIdleTimerLastElementEnabled) {
+ RemoveLocalIdleTimerLastElement();
+ }
+ else {
+ dump("Finishing testing idle API.\n");
+ SimpleTest.finish();
+ }
+ }
+
+ /*
+ * function RemoveLocalIdleTimerLastElement()
+ */
+ function RemoveLocalIdleTimerLastElement()
+ {
+ dump("\n\nTESTING CASE RemoveLocalIdleTimerLastElement\n");
+ dump("=================================\n");
+
+ ResetVars();
+ currTestCaseNum = tcRemoveLocalIdleTimerLastElement;
+ idleServiceObj.idleTime = 1200;
+
+ window.navigator.addIdleObserver(idleObserversArray[1]);
+ idleServiceObj.testIdleBackService(idleObserversArray[1], "idle");
+ }
+
+ function RemoveLocalIdleTimerLastElementCleanUp() {
+ dump("\nRemoveLocalIdleTimerLastElementCleanUp()\n");
+ dump("=====================================\n");
+
+ ok(passed, "Failed test case: RemoveLocalIdleTimerLastElementCleanUp()");
+
+ ResetVars();
+ currTestCaseNum = tcRemoveLocalIdleTimerLastElement;
+ window.navigator.removeIdleObserver(idleObserversArray[2]);
+ window.navigator.removeIdleObserver(idleObserversArray[4]);
+
+ dump("JS RemoveLocalIdleTimerLastElementCleanUp() DONE\n");
+ if (RemoveHeadAfterLastLocalFiredEnabled) {
+ RemoveHeadAfterLastLocalFired();
+ }
+ else {
+ dump("Finishing testing idle API.\n");
+ SimpleTest.finish();
+ }
+ }
+
+ /*
+ - Remove the head after the last local idle timer has been fired
+ - add 1 2 3 4
+ - after 4 has been notified, removed idle observer with time 4
+ - send a back topic
+ - message notification should be 1, 2, 3.
+ */
+ function RemoveHeadAfterLastLocalFired()
+ {
+ dump("\n\nTESTING CASE RemoveHeadAfterLastLocalFired\n");
+ dump("=================================\n");
+
+ ResetVars();
+ currTestCaseNum = tcRemoveHeadAfterLastLocalFired;
+ idleServiceObj.idleTime = 1200;
+
+ window.navigator.addIdleObserver(idleObserversArray[1]);
+ idleServiceObj.testIdleBackService(idleObserversArray[1], "idle");
+ }
+
+ function RemoveHeadAfterLastLocalFiredCleanUp() {
+ dump("\RemoveHeadAfterLastLocalFiredCleanUp()\n");
+ dump("=====================================\n");
+
+ ok(passed, "Failed test case: RemoveHeadAfterLastLocalFiredCleanUp()");
+
+ ResetVars();
+ currTestCaseNum = tcRemoveHeadAfterLastLocalFired;
+
+ window.navigator.removeIdleObserver(idleObserversArray[1]);
+ window.navigator.removeIdleObserver(idleObserversArray[2]);
+ window.navigator.removeIdleObserver(idleObserversArray[3]);
+
+ dump("JS RemoveHeadAfterLastLocalFiredCleanUp() DONE\n");
+ if (RemoveHeadIdleObserverWhileIdleCase1Enabled) {
+ RemoveHeadIdleObserverWhileIdleCase1();
+ }
+ else {
+ dump("Finishing testing idle API.\n");
+ SimpleTest.finish();
+ }
+ }
+
+ function RemoveHeadIdleObserverWhileIdleCase1() {
+ dump("\n\nTESTING CASE RemoveHeadIdleObserverWhileIdleCase1\n");
+ dump("=================================\n");
+
+ ResetVars();
+ currTestCaseNum = tcRemoveHeadIdleObserverWhileIdleCase1;
+ idleServiceObj.idleTime = 1000;
+ window.navigator.addIdleObserver(idleObserversArray[1]);
+ idleServiceObj.testIdleBackService(idleObserversArray[1], "idle");
+ }
+
+ function RemoveHeadIdleObserverWhileIdleCase1CleanUp() {
+ dump("\nRemoveHeadIdleObserverWhileIdleCase1CleanUp()\n");
+ dump("=====================================\n");
+
+ ok(passed, "Failed test case: RemoveHeadIdleObserverWhileIdleCase1CleanUp()");
+
+ ResetVars();
+ currTestCaseNum = tcRemoveHeadIdleObserverWhileIdleCase1;
+
+ for (var i=1; i<4; i++) {
+ window.navigator.removeIdleObserver(idleObserversArray[i]);
+ }
+
+ window.navigator.removeIdleObserver(idleObserversArray[2]);
+ window.navigator.removeIdleObserver(idleObserversArray[3]);
+
+ dump("JS RemoveHeadIdleObserverWhileIdleCase1CleanUp() DONE\n");
+ if (RemoveLastAddLastEnabled) {
+ RemoveLastAddLast();
+ }
+ else {
+ dump("Finishing testing idle API.\n");
+ SimpleTest.finish();
+ }
+ }
+
+ /*
+ * - RemoveLastAddLast()
+ *
+ * - User is currently idle.
+ * - Add callback 1, 2, 3,
+ * - Remove callback 3 after 3200 MS. I.e. after callback 3 has been notified.
+ * - Add callback 4 after 3500 MS
+ * - Output: 1, 2, 3, 4
+ */
+ function RemoveLastAddLast()
+ {
+ dump("\n\nTESTING CASE RemoveLastAddLast()\n");
+ dump("=================================\n");
+
+ ResetVars();
+ currTestCaseNum = tcRemoveLastAddLast;
+ idleServiceObj.idleTime = 1000;
+ window.navigator.addIdleObserver(idleObserversArray[1]);
+ idleServiceObj.testIdleBackService(idleObserversArray[1], "idle");
+ }
+
+ function RemoveLastAddLastCleanUp()
+ {
+ dump("\RemoveLastAddLastCleanUp()\n");
+ dump("=====================================\n");
+
+ ok(passed, "Failed test case: RemoveLastAddLastCleanUp()");
+
+ ResetVars();
+ currTestCaseNum = tcRemoveLastAddLast;
+
+ window.navigator.removeIdleObserver(idleObserversArray[1]);
+ window.navigator.removeIdleObserver(idleObserversArray[2]);
+ window.navigator.removeIdleObserver(idleObserversArray[4]);
+
+ if (numIdleObserversRemoved === 1 && !numIdleObserversAdded) {
+ ok(true, "Failed test case: RemoveLastAddLastCleanUp()");
+ }
+ else {
+ ok(false, "Failed test case: RemoveLastAddLastCleanUp()");
+ }
+
+
+ try {
+ componentMgr.unregisterFactory(idleServiceCID, idleServiceObj);
+ }
+ catch(err) {
+ dump("test_bug715041_removal.xul: RemoveLastAddLastCleanUp() Failed to unregister factory, mock idle service!\n");
+ }
+
+ try {
+ componentMgr.registerFactory(oldIdleServiceCID, "Re registering old idle service", idleServiceContractID, oldIdleServiceFactoryObj);
+ }
+ catch(err) {
+ dump("test_bug715041_removal.xul: RemoveLastAddLastCleanUp() Failed to register factory, original idle service!\n");
+ }
+
+ dump("JS RemoveLastAddLastCleanUp() DONE\n");
+ dump("Finishing testing idle API.\n");
+ SimpleTest.finish();
+ }
+
+
+ // Registering new moch JS idle service
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.requestLongerTimeout(10);
+
+ try {
+ var idleServiceCID = Components.ID("0fdc1bbf-3868-4660-9855-0c2e376922bc");
+ var idleServiceContractID = "@mozilla.org/widget/idleservice;1";
+ var oldIdleService = Components.classes[idleServiceContractID].getService(Components.interfaces.nsIIdleService);
+ }
+ catch(ex) {
+ dump("test_bug715041_removal.xul: 1) Failed to get old idle service.\n");
+ }
+
+ try {
+ // Registering new moch JS idle service
+ var componentMgr = Components.manager.QueryInterface(Components.interfaces.nsIComponentRegistrar);
+ }
+ catch(err) {
+ dump("test_bug715041_removal.xul: Failed to query component registrar interface.\n");
+ }
+
+ try {
+ var oldIdleServiceFactoryObj = componentMgr.getClassObjectByContractID(idleServiceContractID, Components.interfaces.nsIFactory);
+ }
+ catch(err) {
+ dump("test_bug715041_removal.xul: Failed to get old idle service.\n");
+ }
+
+ try {
+ var oldIdleServiceCID = componentMgr.contractIDToCID(idleServiceContractID);
+ }
+ catch(err) {
+ dump("test_bug715041._removalxul: Failed to convert ID to CID for old idle service.\n");
+ }
+
<