Fixing bug 715041. Add support for Idle API. r=bent, jst
authorBonnie Surender <bsurender@mozilla.com>
Fri, 29 Jun 2012 01:32:21 -0700
changeset 97920 b405f493e834918e5513731d96975e1de7b5bbeb
parent 97919 bdafb1929d8d44edced52b665088428d89608c80
child 97921 6c5cfab6e24134423d44cb9fc107d4ce238aece3
push id11281
push userjst@mozilla.com
push dateFri, 29 Jun 2012 08:32:50 +0000
treeherdermozilla-inbound@b405f493e834 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbent, jst
bugs715041
milestone16.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Fixing bug 715041. Add support for Idle API. r=bent, jst
content/base/public/nsContentUtils.h
content/base/src/nsContentUtils.cpp
dom/base/Navigator.cpp
dom/base/nsGlobalWindow.cpp
dom/base/nsGlobalWindow.h
dom/base/nsPIDOMWindow.h
dom/base/test/Makefile.in
dom/base/test/test_bug715041.xul
dom/base/test/test_bug715041_removal.xul
dom/interfaces/base/Makefile.in
dom/interfaces/base/nsIDOMNavigator.idl
dom/interfaces/base/nsIIdleObserver.idl
modules/libpref/src/init/all.js
widget/Makefile.in
widget/android/nsIdleServiceAndroid.cpp
widget/android/nsIdleServiceAndroid.h
widget/android/nsWidgetFactory.cpp
widget/android/nsWindow.cpp
widget/android/nsWindow.h
widget/cocoa/nsIdleServiceX.h
widget/cocoa/nsWidgetFactory.mm
widget/gonk/nsIdleServiceGonk.cpp
widget/gonk/nsIdleServiceGonk.h
widget/gonk/nsWidgetFactory.cpp
widget/gonk/nsWindow.cpp
widget/gonk/nsWindow.h
widget/gtk2/nsIdleServiceGTK.cpp
widget/gtk2/nsIdleServiceGTK.h
widget/gtk2/nsWidgetFactory.cpp
widget/gtk2/nsWindow.cpp
widget/nsGUIEvent.h
widget/nsIIdleService.idl
widget/nsIIdleServiceInternal.idl
widget/os2/nsIdleServiceOS2.cpp
widget/os2/nsIdleServiceOS2.h
widget/os2/nsWidgetFactory.cpp
widget/qt/nsIdleServiceQt.cpp
widget/qt/nsIdleServiceQt.h
widget/qt/nsWidgetFactory.cpp
widget/qt/nsWindow.cpp
widget/qt/nsWindow.h
widget/windows/nsIdleServiceWin.cpp
widget/windows/nsIdleServiceWin.h
widget/windows/nsWidgetFactory.cpp
widget/windows/nsWindow.cpp
widget/windows/nsWindow.h
widget/xpwidgets/nsIdleService.cpp
widget/xpwidgets/nsIdleService.h
xpcom/glue/nsTObserverArray.h
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -1826,16 +1826,21 @@ public:
    * Returns true if requests for full-screen are allowed in the current
    * context. Requests are only allowed if the user initiated them (like with
    * a mouse-click or key press), unless this check has been disabled by
    * setting the pref "full-screen-api.allow-trusted-requests-only" to false.
    */
   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
    * control? This always returns false on MacOSX.
    */
   static bool HasPluginWithUncontrolledEventDispatch(nsIDocument* aDoc);
 
   /**
@@ -2008,16 +2013,29 @@ public:
    * Returns true if the language name is a version of JavaScript and
    * false otherwise
    */
   static bool IsJavaScriptLanguage(const nsString& aName, PRUint32 *aVerFlags);
 
   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
    * seperated list of app urls that have permission to use whatever
    * the preference refers to (for example, does the current window
    * have access to mozTelephony). Chrome is always given permissions
    * for the requested preference. Sets aAllowed based on preference.
    *
@@ -2134,16 +2152,17 @@ private:
 
   static nsIInterfaceRequestor* sSameOriginChecker;
 
   static bool sIsHandlingKeyBoardEvent;
   static bool sAllowXULXBL_for_file;
   static bool sIsFullScreenApiEnabled;
   static bool sTrustedFullScreenOnly;
   static PRUint32 sHandlingInputTimeout;
+  static bool sIsIdleObserverAPIEnabled;
 
   static nsHtml5StringParser* sHTMLFragmentParser;
   static nsIParser* sXMLFragmentParser;
   static nsIFragmentContentSink* sXMLFragmentSink;
 
   /**
    * True if there's a fragment parser activation on the stack.
    */
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -24,16 +24,17 @@
 #include "nsDOMCID.h"
 #include "nsContentUtils.h"
 #include "nsIXPConnect.h"
 #include "nsIContent.h"
 #include "mozilla/dom/Element.h"
 #include "nsIDocument.h"
 #include "nsINodeInfo.h"
 #include "nsReadableUtils.h"
+#include "nsIIdleService.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMNodeList.h"
 #include "nsIDOMNode.h"
 #include "nsIIOService.h"
 #include "nsNetCID.h"
 #include "nsNetUtil.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsDOMError.h"
@@ -238,16 +239,17 @@ nsString* nsContentUtils::sShiftText = n
 nsString* nsContentUtils::sControlText = nsnull;
 nsString* nsContentUtils::sMetaText = nsnull;
 nsString* nsContentUtils::sAltText = nsnull;
 nsString* nsContentUtils::sModifierSeparator = nsnull;
 
 bool nsContentUtils::sInitialized = false;
 bool nsContentUtils::sIsFullScreenApiEnabled = false;
 bool nsContentUtils::sTrustedFullScreenOnly = true;
+bool nsContentUtils::sIsIdleObserverAPIEnabled = false;
 
 PRUint32 nsContentUtils::sHandlingInputTimeout = 1000;
 
 nsHtml5StringParser* nsContentUtils::sHTMLFragmentParser = nsnull;
 nsIParser* nsContentUtils::sXMLFragmentParser = nsnull;
 nsIFragmentContentSink* nsContentUtils::sXMLFragmentSink = nsnull;
 bool nsContentUtils::sFragmentParsingActive = false;
 
@@ -413,16 +415,18 @@ nsContentUtils::Init()
                                "dom.allow_XUL_XBL_for_file");
 
   Preferences::AddBoolVarCache(&sIsFullScreenApiEnabled,
                                "full-screen-api.enabled");
 
   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);
 
   nsGenericElement::InitCCCallbacks();
 
   sInitialized = true;
 
@@ -864,16 +868,32 @@ nsContentUtils::SplitMimeType(const nsAS
     aParams.StripWhitespace();
   }
   else {
     aType = aValue;
   }
   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.
  */
 /* static */
 nsIParserService*
 nsContentUtils::GetParserService()
 {
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -32,16 +32,17 @@
 #include "nsIPowerManagerService.h"
 #include "SmsManager.h"
 #include "nsISmsService.h"
 #include "mozilla/Hal.h"
 #include "nsIWebNavigation.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "Connection.h"
 #include "MobileConnection.h"
+#include "nsIIdleObserver.h"
 
 #ifdef MOZ_MEDIA_NAVIGATOR
 #include "MediaManager.h"
 #endif
 #ifdef MOZ_B2G_RIL
 #include "TelephonyFactory.h"
 #endif
 #ifdef MOZ_B2G_BT
@@ -646,16 +647,53 @@ GetVibrationDurationFromJsval(const jsva
 {
   return JS_ValueToInt32(cx, aJSVal, aOut) &&
          *aOut >= 0 && static_cast<uint32_t>(*aOut) <= sMaxVibrateMS;
 }
 
 } // 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);
   NS_ENSURE_TRUE(win, NS_OK);
 
   nsIDOMDocument* domDoc = win->GetExtantDocument();
   NS_ENSURE_TRUE(domDoc, NS_ERROR_FAILURE);
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -15,16 +15,17 @@
 #include "nsScreen.h"
 #include "nsHistory.h"
 #include "nsPerformance.h"
 #include "nsDOMNavigationTiming.h"
 #include "nsBarProps.h"
 #include "nsDOMStorage.h"
 #include "nsDOMOfflineResourceList.h"
 #include "nsDOMError.h"
+#include "nsIIdleService.h"
 
 #ifdef XP_WIN
 #ifdef GetClassName
 #undef GetClassName
 #endif // GetClassName
 #endif // XP_WIN
 
 // Helper Classes
@@ -215,16 +216,17 @@
 #include "nsRefreshDriver.h"
 #include "mozAutoDocUpdate.h"
 
 #include "mozilla/Telemetry.h"
 #include "nsLocation.h"
 #include "nsWrapperCacheInlines.h"
 #include "nsDOMEventTargetHelper.h"
 #include "nsIAppsService.h"
+#include "prrng.h"
 
 #ifdef ANDROID
 #include <android/log.h>
 #endif
 
 #ifdef PR_LOGGING
 static PRLogModuleInfo* gDOMLeakPRLog;
 #endif
@@ -233,16 +235,17 @@ static const char kStorageEnabled[] = "d
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using mozilla::TimeStamp;
 using mozilla::TimeDuration;
 
 nsGlobalWindow::WindowByIdTable *nsGlobalWindow::sWindowsById = nsnull;
 bool nsGlobalWindow::sWarnedAboutWindowInternal = false;
+bool nsGlobalWindow::sIdleObserversAPIFuzzTimeDisabled = false;
 
 static nsIEntropyCollector *gEntropyCollector          = nsnull;
 static PRInt32              gRefCnt                    = 0;
 static PRInt32              gOpenPopupSpamCount        = 0;
 static PopupControlState    gPopupControlState         = openAbused;
 static PRInt32              gRunningTimeoutDepth       = 0;
 static bool                 gMouseDown                 = false;
 static bool                 gDragServiceDisabled       = false;
@@ -490,32 +493,42 @@ nsDOMMozURLProperty::RevokeObjectURL(con
 
   return NS_OK;
 }
 
 /**
  * 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
   NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aData)
   {
     if (!mWindow)
       return NS_OK;
     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()
 {
 #ifdef DEBUG_jst
   {
     extern int gTimeoutCnt;
 
     ++gTimeoutCnt;
@@ -623,25 +636,31 @@ NewOuterWindowProxy(JSContext *cx, JSObj
 }
 
 //*****************************************************************************
 //***    nsGlobalWindow: Object Management
 //*****************************************************************************
 
 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),
     mNeedsFocus(true),
     mHasFocus(false),
 #if defined(XP_MACOSX)
     mShowAccelerators(false),
     mShowFocusRings(false),
@@ -718,16 +737,19 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalW
                                  "browser.dom.window.dump.enabled");
 #endif
     Preferences::AddIntVarCache(&gMinTimeoutValue,
                                 "dom.min_timeout_value",
                                 DEFAULT_MIN_TIMEOUT_VALUE);
     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) {
     const nsAdoptingCString& fname =
       Preferences::GetCString("browser.dom.window.dump.file");
     if (!fname.IsEmpty()) {
       // if this fails to open, Dump() knows to just go to stdout
       // on null.
@@ -951,27 +973,31 @@ nsGlobalWindow::CleanUp(bool aIgnoreModa
       return;
     }
   }
 
   // Guarantee idempotence.
   if (mCleanedUp)
     return;
   mCleanedUp = true;
-  
+
   mEventTargetObjects.EnumerateEntries(DisconnectEventTargetObjects, nsnull);
   mEventTargetObjects.Clear();
 
   if (mObserver) {
     nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
     if (os) {
       os->RemoveObserver(mObserver, NS_IOSERVICE_OFFLINE_STATUS_TOPIC);
       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();
     NS_RELEASE(mObserver);
   }
 
   mNavigator = nsnull;
   mScreen = nsnull;
@@ -1019,16 +1045,21 @@ nsGlobalWindow::CleanUp(bool aIgnoreModa
 
   mInnerWindowHolder = nsnull;
   mArguments = nsnull;
   mArgumentsLast = nsnull;
   mArgumentsOrigin = nsnull;
 
   CleanupCachedXBLHandlers(this);
 
+  if (mIdleTimer) {
+    mIdleTimer->Cancel();
+    mIdleTimer = nsnull;
+  }
+
 #ifdef DEBUG
   nsCycleCollector_DEBUG_shouldBeFreed(static_cast<nsIScriptGlobalObject*>(this));
 #endif
 }
 
 void
 nsGlobalWindow::ClearControllers()
 {
@@ -1068,16 +1099,23 @@ nsGlobalWindow::FreeInnerObjects()
   indexedDB::IndexedDatabaseManager* idbManager =
     indexedDB::IndexedDatabaseManager::Get();
   if (idbManager) {
     idbManager->AbortCloseDatabasesForWindow(this);
   }
 
   ClearAllTimeouts();
 
+  if (mIdleTimer) {
+    mIdleTimer->Cancel();
+    mIdleTimer = nsnull;
+  }
+
+  mIdleObservers.Clear();
+
   mChromeEventHandler = nsnull;
 
   if (mListenerManager) {
     mListenerManager->Disconnect();
     mListenerManager = nsnull;
   }
 
   mLocation = nsnull;
@@ -1231,27 +1269,32 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
     cb.NoteNativeChild(timeout, NS_CYCLE_COLLECTION_PARTICIPANT(nsTimeout));
   }
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mLocalStorage)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mSessionStorage)
   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)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mParentTarget)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDocument)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mFrameElement)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mFocusedNode)
 
   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)
   nsGlobalWindow::CleanupCachedXBLHandlers(tmp);
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mContext)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mControllers)
@@ -1277,19 +1320,22 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
 
   // Unlink stuff from nsPIDOMWindow
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mChromeEventHandler)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mParentTarget)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDocument)
   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
 {
   TraceData(TraceCallback& aCallback, void* aClosure) :
     callback(aCallback), closure(aClosure) {}
 
   TraceCallback& callback;
@@ -2467,16 +2513,25 @@ nsGlobalWindow::PreHandleEvent(nsEventCh
       if (ds) {
         gDragServiceDisabled = false;
         ds->Unsuppress();
       }
     }
   }
 
   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;
 }
 
 bool
 nsGlobalWindow::DialogOpenAttempted()
 {
   nsGlobalWindow *topWindow = GetScriptableTop();
   if (!topWindow) {
@@ -8375,31 +8430,456 @@ nsGlobalWindow::FireOfflineStatusEvent()
     mDocument->GetDocumentElement(getter_AddRefs(documentElement));
     if(documentElement) {        
       eventTarget = documentElement;
     }
   }
   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)
 {
   if (!nsCRT::strcmp(aTopic, NS_IOSERVICE_OFFLINE_STATUS_TOPIC)) {
     if (IsFrozen()) {
       // if an even number of notifications arrive while we're frozen,
       // we don't need to fire.
       mFireOfflineStatusChangeEventOnThaw = !mFireOfflineStatusChangeEventOnThaw;
     } else {
       FireOfflineStatusEvent();
     }
     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;
 
     nsCOMPtr<nsIDOMStorageEvent> event = do_QueryInterface(aSubject, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsIDOMStorage> changingStorage;
@@ -8558,16 +9038,26 @@ nsGlobalWindow::FireDelayedDOMEvents()
     static_cast<nsDOMOfflineResourceList*>(mApplicationCache.get())->FirePendingEvents();
   }
 
   if (mFireOfflineStatusChangeEventOnThaw) {
     mFireOfflineStatusChangeEventOnThaw = false;
     FireOfflineStatusEvent();
   }
 
+  if (mNotifyIdleObserversIdleOnThaw) {
+    mNotifyIdleObserversIdleOnThaw = false;
+    ScheduleIdleTimerCallback();
+  }
+
+  if (mNotifyIdleObserversActiveOnThaw) {
+    mNotifyIdleObserversActiveOnThaw = false;
+    ScheduleActiveTimerCallback();
+  }
+
   nsCOMPtr<nsIDocShellTreeNode> node =
     do_QueryInterface(GetDocShell());
   if (node) {
     PRInt32 childCount = 0;
     node->GetChildCount(&childCount);
 
     for (PRInt32 i = 0; i < childCount; ++i) {
       nsCOMPtr<nsIDocShellTreeItem> childShell;
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -59,31 +59,38 @@
 #include "nsIContent.h"
 #include "nsIIDBFactory.h"
 #include "nsFrameMessageManager.h"
 #include "mozilla/TimeStamp.h"
 #include "nsIDOMTouchEvent.h"
 #include "nsIInlineEventHandlers.h"
 #include "nsWrapperCacheInlines.h"
 #include "nsIDOMApplicationRegistry.h"
+#include "nsIIdleObserver.h"
 
 // JS includes
 #include "jsapi.h"
 
 #define DEFAULT_HOME_PAGE "www.mozilla.org"
 #define PREF_BROWSER_STARTUP_HOMEPAGE "browser.startup.homepage"
 
 // Amount of time allowed between alert/prompt/confirm before enabling
 // the stop dialog checkbox.
 #define SUCCESSIVE_DIALOG_TIME_LIMIT 3 // 3 sec
 
 // During click or mousedown events (and others, see nsDOMEvent) we allow modal
 // 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;
 class nsIDOMEvent;
 class nsIScrollableFrame;
 class nsIControllers;
 
 class nsBarProp;
@@ -96,16 +103,17 @@ class WindowStateHolder;
 class nsGlobalWindowObserver;
 class nsGlobalWindow;
 class PostMessageEvent;
 class nsRunnable;
 class nsDOMEventTargetHelper;
 class nsDOMOfflineResourceList;
 class nsDOMMozURLProperty;
 class nsDOMWindowUtils;
+class nsIIdleService;
 
 #ifdef MOZ_DISABLE_DOMCRYPTO
 class nsIDOMCrypto;
 #endif
 
 class nsWindowSizes;
 
 namespace mozilla {
@@ -190,16 +198,45 @@ struct nsTimeout : PRCList
   // The language-specific information about the callback.
   nsCOMPtr<nsIScriptTimeoutHandler> mScriptHandler;
 
 private:
   // reference count for shared usage
   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
 //*****************************************************************************
 // Beware that all scriptable interfaces implemented by
 // nsGlobalWindow will be reachable from JS, if you make this class
 // implement new interfaces you better know what you're
 // doing. Security wise this is very sensitive code. --
 // jst@netscape.com
@@ -541,17 +578,49 @@ public:
 
   void SizeOfIncludingThis(nsWindowSizes* aWindowSizes) const;
 
   void UnmarkGrayTimers();
 
   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;
 
   enum TriState {
     TriState_Unknown = -1,
     TriState_False,
     TriState_True
   };
@@ -691,16 +760,27 @@ protected:
                             bool *aFreeSecurityPass, JSContext **aCXused);
   bool PopupWhitelisted();
   PopupControlState RevisePopupAbuseLevel(PopupControlState);
   void     FireAbuseEvents(bool aBlocked, bool aWindow,
                            const nsAString &aPopupURL,
                            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);
   void EnsureReflowFlushAndPaint();
   nsresult CheckSecurityWidthAndHeight(PRInt32* width, PRInt32* height);
   nsresult CheckSecurityLeftAndTop(PRInt32* left, PRInt32* top);
 
   // Arguments to this function should have values in app units
@@ -716,17 +796,17 @@ protected:
 
   bool     GetBlurSuppression();
 
   // If aDoFlush is true, we'll flush our own layout; otherwise we'll try to
   // just flush our parent and only flush ourselves if we think we need to.
   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();
 
   bool IsFrame()
   {
     return GetParentInternal() != nsnull;
   }
@@ -793,17 +873,17 @@ protected:
 
   already_AddRefed<nsPIWindowRoot> GetTopWindowRoot();
 
   static void NotifyDOMWindowDestroyed(nsGlobalWindow* aWindow);
   void NotifyWindowIDDestroyed(const char* aTopic);
 
   static void NotifyDOMWindowFrozen(nsGlobalWindow* aWindow);
   static void NotifyDOMWindowThawed(nsGlobalWindow* aWindow);
-  
+
   void ClearStatus();
 
   virtual void UpdateParentTarget();
 
   bool GetIsTabModalPromptAllowed();
 
   inline PRInt32 DOMMinTimeoutValue() const;
 
@@ -827,17 +907,17 @@ protected:
 
   // This member is also used on both inner and outer windows, but
   // for slightly different purposes. On inner windows it means the
   // inner window is held onto by session history and should not
   // change. On outer windows it means that the window is in a state
   // 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;
   bool                          mIsClosed : 1;
   bool                          mInClose : 1;
   // mHavePendingClose means we've got a termination function set to
   // close us when the JS stops executing or that we have a close
   // event posted.  If this is set, just ignore window.close() calls.
@@ -845,16 +925,18 @@ protected:
   bool                          mHadOriginalOpener : 1;
   bool                          mIsPopupSpam : 1;
 
   // Indicates whether scripts are allowed to close this window.
   bool                          mBlockScriptedClosingFlag : 1;
 
   // 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.
   bool                          mCreatingInnerWindow : 1;
 
   // Fast way to tell if this is a chrome window (without having to QI).
   bool                          mIsChrome : 1;
 
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -11,23 +11,23 @@
 #include "nsIDOMWindow.h"
 
 #include "nsIDOMLocation.h"
 #include "nsIDOMXULCommandDispatcher.h"
 #include "nsIDOMElement.h"
 #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
 // permissive to least permissive so that it's safe to push state in
 // all situations. Pushing popup state onto the stack never makes the
 // current popup state less permissive (see
 // nsGlobalWindow::PushPopupControlState()).
 enum PopupControlState {
@@ -43,18 +43,18 @@ class nsIDocument;
 class nsIScriptTimeoutHandler;
 struct nsTimeout;
 template <class> class nsScriptObjectHolder;
 class nsXBLPrototypeHandler;
 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
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_PIDOMWINDOW_IID)
 
   virtual nsPIDOMWindow* GetPrivateRoot() = 0;
 
@@ -65,16 +65,19 @@ public:
 
   virtual void SetActive(bool aActive)
   {
     NS_PRECONDITION(IsOuterWindow(),
                     "active state is only maintained on outer windows");
     mIsActive = aActive;
   }
 
+  virtual nsresult RegisterIdleObserver(nsIIdleObserver* aIdleObserver) = 0;
+  virtual nsresult UnregisterIdleObserver(nsIIdleObserver* aIdleObserver) = 0;
+
   bool IsActive()
   {
     NS_PRECONDITION(IsOuterWindow(),
                     "active state is only maintained on outer windows");
     return mIsActive;
   }
 
   virtual void SetIsBackground(bool aIsBackground)
--- a/dom/base/test/Makefile.in
+++ b/dom/base/test/Makefile.in
@@ -14,11 +14,19 @@ include $(topsrcdir)/config/rules.mk
 TEST_FILES = \
   test_domrequest.html \
   test_gsp-standards.html \
   test_gsp-quirks.html \
   test_nondomexception.html \
   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)
+
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_bug715041.xul
@@ -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>
+
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_bug715041_removal.xul
@@ -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");
+  }
+
+  try {
+    componentMgr.unregisterFactory(oldIdleServiceCID, oldIdleServiceFactoryObj);
+  }
+  catch(err) {
+    dump("test_bug715041_removal.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_removal.xul: Failed to register mock idle service.\n");
+  }
+
+  //test case enabled
+  var RemoveLocalIdleObserverWhileIdleEnabled = true;
+  var RemoveHeadIdleObserverEnabled = true;
+  var RemoveLocalIdleTimerWhileIdleEnabled = true;
+  var RemoveLocalIdleTimerLastElementEnabled = true;
+  var RemoveHeadAfterLastLocalFiredEnabled = true;
+  var RemoveHeadIdleObserverWhileIdleCase1Enabled = true;
+  var RemoveLastAddLastEnabled = true;
+
+  RemoveHeadIdleObserverWhileActive();
+
+]]>
+    </script>
+    </window>
+
--- a/dom/interfaces/base/Makefile.in
+++ b/dom/interfaces/base/Makefile.in
@@ -52,13 +52,14 @@ XPIDLSRCS =					\
 	nsITabChild.idl				\
 	nsITabParent.idl			\
 	nsIDOMGlobalPropertyInitializer.idl	\
 	nsIDOMGlobalObjectConstructor.idl \
 	nsIStructuredCloneContainer.idl		\
 	nsIDOMPerformance.idl			\
 	nsIDOMPerformanceTiming.idl		\
 	nsIDOMPerformanceNavigation.idl		\
+	nsIIdleObserver.idl			\
 	$(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
 XPIDL_FLAGS += -I$(topsrcdir)/dom/interfaces/events/
--- a/dom/interfaces/base/nsIDOMNavigator.idl
+++ b/dom/interfaces/base/nsIDOMNavigator.idl
@@ -1,16 +1,18 @@
 /* -*- 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(e0737ed5-89c5-4fe3-891e-a75bf3a1bb55)]
+interface nsIIdleObserver;
+
+[scriptable, uuid(c148ed5a-31c1-4a21-a13b-341f592579ff)]
 interface nsIDOMNavigator : nsISupports
 {
   readonly attribute DOMString             appCodeName;
   readonly attribute DOMString             appName;
   readonly attribute DOMString             appVersion;
   readonly attribute DOMString             language;
   readonly attribute nsIDOMMimeTypeArray   mimeTypes;
   readonly attribute DOMString             platform;
@@ -73,16 +75,26 @@ interface nsIDOMNavigator : nsISupports
    *   instead of a_n), the final element doesn't specify anything meaningful.
    *
    *   We may throw NS_ERROR_DOM_NOT_SUPPORTED_ERR if the vibration pattern is
    *   too long, or if any of its elements is too large.
    */
   [implicit_jscontext]
   void mozVibrate(in jsval aPattern);
 
+  /** 
+   * 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.
    *
    * A page holds a wake lock to request that a resource not be turned
    * off (or otherwise made unavailable).
    *
    * The topic is the name of a resource that might be made unavailable for
    * various reasons. For example, on a mobile device the power manager might
new file mode 100644
--- /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();
+};
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -3540,16 +3540,19 @@ pref("alerts.totalOpenTime", 4000);
 pref("alerts.disableSlidingEffect", false);
 
 // DOM full-screen API.
 pref("full-screen-api.enabled", false);
 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);
  
 //3D Transforms
 pref("layout.3d-transforms.enabled", true);
 
 pref("dom.vibrator.enabled", true);
--- a/widget/Makefile.in
+++ b/widget/Makefile.in
@@ -114,16 +114,17 @@ XPIDLSRCS	= \
 		nsIBidiKeyboard.idl \
 		nsIScreen.idl \
 		nsIScreenManager.idl \
 		nsIPrintSession.idl \
 		nsIPrintSettings.idl \
 		nsIPrintSettingsService.idl \
 		nsIPrintOptions.idl \
 		nsIIdleService.idl \
+		nsIIdleServiceInternal.idl \
 		nsIGfxInfo.idl \
                 nsIGfxInfoDebug.idl \
 		nsIIMEPicker.idl \
 		$(NULL)
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
 XPIDLSRCS	+= nsIPrintSettingsWin.idl \
 		nsIWinTaskbar.idl	\
--- a/widget/android/nsIdleServiceAndroid.cpp
+++ b/widget/android/nsIdleServiceAndroid.cpp
@@ -3,17 +3,17 @@
  */
 /* 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 "nsIdleServiceAndroid.h"
 #include "nsIServiceManager.h"
 
-NS_IMPL_ISUPPORTS2(nsIdleServiceAndroid, nsIIdleService, nsIdleService)
+NS_IMPL_ISUPPORTS_INHERITED0(nsIdleServiceAndroid, nsIdleService)
 
 bool
 nsIdleServiceAndroid::PollIdleTime(PRUint32 *aIdleTime)
 {
     return false;
 }
 
 bool
--- a/widget/android/nsIdleServiceAndroid.h
+++ b/widget/android/nsIdleServiceAndroid.h
@@ -8,16 +8,31 @@
 #ifndef nsIdleServiceAndroid_h__
 #define nsIdleServiceAndroid_h__
 
 #include "nsIdleService.h"
 
 class nsIdleServiceAndroid : public nsIdleService
 {
 public:
-    NS_DECL_ISUPPORTS
+    NS_DECL_ISUPPORTS_INHERITED
 
     bool PollIdleTime(PRUint32* aIdleTime);
+
+    static already_AddRefed<nsIdleServiceAndroid> GetInstance() 
+    {
+        nsIdleServiceAndroid* idleService = 
+            static_cast<nsIdleServiceAndroid*>(nsIdleService::GetInstance().get());
+        if (!idleService) {
+            idleService = new nsIdleServiceAndroid();
+            NS_ADDREF(idleService);
+        }
+        
+        return idleService;
+    }
+
 protected:
+    nsIdleServiceAndroid() { }
+    virtual ~nsIdleServiceAndroid() { }
     bool UsePollMode();
 };
 
 #endif // nsIdleServiceAndroid_h__
--- a/widget/android/nsWidgetFactory.cpp
+++ b/widget/android/nsWidgetFactory.cpp
@@ -25,17 +25,17 @@
 #include "nsFilePicker.h"
 #include "nsHTMLFormatConverter.h"
 #include "nsIMEPicker.h"
 #include "nsFilePickerProxy.h"
 #include "nsXULAppAPI.h"
 
 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)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrintOptionsAndroid, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrintSession, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsDeviceContextSpecAndroid)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsHTMLFormatConverter)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsIMEPicker)
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -2055,17 +2055,17 @@ nsWindow::FindWindowForPoint(const nsInt
 void
 nsWindow::UserActivity()
 {
   if (!mIdleService) {
     mIdleService = do_GetService("@mozilla.org/widget/idleservice;1");
   }
 
   if (mIdleService) {
-    mIdleService->ResetIdleTimeOut();
+    mIdleService->ResetIdleTimeOut(0);
   }
 }
 
 NS_IMETHODIMP
 nsWindow::ResetInputState()
 {
     //ALOGIME("IME: ResetInputState: s=%d", aState);
 
--- a/widget/android/nsWindow.h
+++ b/widget/android/nsWindow.h
@@ -3,26 +3,25 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef NSWINDOW_H_
 #define NSWINDOW_H_
 
 #include "nsBaseWidget.h"
 #include "gfxPoint.h"
-
+#include "nsIIdleServiceInternal.h"
 #include "nsTArray.h"
 
 #ifdef MOZ_JAVA_COMPOSITOR
 #include "AndroidJavaWrappers.h"
 #include "Layers.h"
 #endif
 
 class gfxASurface;
-class nsIdleService;
 
 struct ANPEvent;
 
 namespace mozilla {
     class AndroidGeckoEvent;
     class AndroidKeyEvent;
 
     namespace layers {
@@ -183,17 +182,17 @@ protected:
     double mStartDist;
     double mLastDist;
     nsAutoPtr<nsIntPoint> mStartPoint;
 
     // Multitouch swipe thresholds in screen pixels
     double mSwipeMaxPinchDelta;
     double mSwipeMinDistance;
 
-    nsCOMPtr<nsIdleService> mIdleService;
+    nsCOMPtr<nsIIdleServiceInternal> mIdleService;
 
     bool mIMEComposing;
     nsString mIMEComposingText;
     nsString mIMELastDispatchedComposingText;
     nsAutoTArray<nsTextRange, 4> mIMERanges;
 
     InputContext mInputContext;
 
--- a/widget/cocoa/nsIdleServiceX.h
+++ b/widget/cocoa/nsIdleServiceX.h
@@ -5,20 +5,31 @@
 #ifndef nsIdleServiceX_h_
 #define nsIdleServiceX_h_
 
 #include "nsIdleService.h"
 
 class nsIdleServiceX : public nsIdleService
 {
 public:
-  NS_DECL_ISUPPORTS
-
-  nsIdleServiceX() {}
-  virtual ~nsIdleServiceX() {}
+  NS_DECL_ISUPPORTS_INHERITED
 
   bool PollIdleTime(PRUint32* aIdleTime);
 
+  static already_AddRefed<nsIdleServiceX> GetInstance() 
+  {
+    nsIdleServiceX* idleService = 
+      static_cast<nsIdleServiceX*>(nsIdleService::GetInstance().get());
+    if (!idleService) {
+      idleService = new nsIdleServiceX();
+      NS_ADDREF(idleService);
+    }
+    
+    return idleService;
+  }
+  
 protected:
+    nsIdleServiceX() { }
+    virtual ~nsIdleServiceX() { }
     bool UsePollMode();
 };
 
 #endif // nsIdleServiceX_h_
--- a/widget/cocoa/nsWidgetFactory.mm
+++ b/widget/cocoa/nsWidgetFactory.mm
@@ -43,17 +43,17 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsHTMLFor
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsClipboard)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsClipboardHelper)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsDragService)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsScreenManagerCocoa)
 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)
 
 #include "nsBidiKeyboard.h"
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsBidiKeyboard)
 
 #include "nsNativeThemeCocoa.h"
--- a/widget/gonk/nsIdleServiceGonk.cpp
+++ b/widget/gonk/nsIdleServiceGonk.cpp
@@ -3,17 +3,17 @@
  */
 /* 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 "nsIdleServiceGonk.h"
 #include "nsIServiceManager.h"
 
-NS_IMPL_ISUPPORTS2(nsIdleServiceGonk, nsIIdleService, nsIdleService)
+NS_IMPL_ISUPPORTS_INHERITED0(nsIdleServiceGonk, nsIdleService)
 
 bool
 nsIdleServiceGonk::PollIdleTime(PRUint32 *aIdleTime)
 {
     return false;
 }
 
 bool
--- a/widget/gonk/nsIdleServiceGonk.h
+++ b/widget/gonk/nsIdleServiceGonk.h
@@ -8,16 +8,31 @@
 #ifndef nsIdleServiceGonk_h__
 #define nsIdleServiceGonk_h__
 
 #include "nsIdleService.h"
 
 class nsIdleServiceGonk : public nsIdleService
 {
 public:
-    NS_DECL_ISUPPORTS
+    NS_DECL_ISUPPORTS_INHERITED
 
     bool PollIdleTime(PRUint32* aIdleTime);
+
+    static already_AddRefed<nsIdleServiceGonk> GetInstance()
+    {
+        nsIdleServiceGonk* idleService =
+            static_cast<nsIdleServiceGonk*>(nsIdleService::GetInstance().get());
+        if (!idleService) {
+            idleService = new nsIdleServiceGonk();
+            NS_ADDREF(idleService);
+        }
+
+        return idleService;
+    }
+
 protected:
+    nsIdleServiceGonk() { }
+    virtual ~nsIdleServiceGonk() { }
     bool UsePollMode();
 };
 
 #endif // nsIdleServiceGonk_h__
--- a/widget/gonk/nsWidgetFactory.cpp
+++ b/widget/gonk/nsWidgetFactory.cpp
@@ -24,17 +24,17 @@
 #include "PuppetWidget.h"
 
 using namespace mozilla::widget;
 
 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);
 NS_DEFINE_NAMED_CID(NS_WINDOW_CID);
 NS_DEFINE_NAMED_CID(NS_CHILD_CID);
 NS_DEFINE_NAMED_CID(NS_SCREENMANAGER_CID);
 NS_DEFINE_NAMED_CID(NS_HTMLFORMATCONVERTER_CID);
 NS_DEFINE_NAMED_CID(NS_IDLE_SERVICE_CID);
--- a/widget/gonk/nsWindow.cpp
+++ b/widget/gonk/nsWindow.cpp
@@ -554,17 +554,17 @@ nsWindow::BringToTop()
 void
 nsWindow::UserActivity()
 {
     if (!mIdleService) {
         mIdleService = do_GetService("@mozilla.org/widget/idleservice;1");
     }
 
     if (mIdleService) {
-        mIdleService->ResetIdleTimeOut();
+        mIdleService->ResetIdleTimeOut(0);
     }
 }
 
 PRUint32
 nsWindow::GetGLFrameBufferFormat()
 {
     if (mLayerManager &&
         mLayerManager->GetBackendType() == LayerManager::LAYERS_OPENGL) {
--- a/widget/gonk/nsWindow.h
+++ b/widget/gonk/nsWindow.h
@@ -1,21 +1,20 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsWindow_h
 #define nsWindow_h
 
 #include "nsBaseWidget.h"
+#include "nsIIdleServiceInternal.h"
 
 extern nsIntRect gScreenBounds;
 
-class nsIdleService;
-
 namespace mozilla {
 namespace gl {
 class GLContext;
 }
 namespace layers {
 class LayersManager;
 }
 }
@@ -95,17 +94,17 @@ public:
 
     virtual PRUint32 GetGLFrameBufferFormat() MOZ_OVERRIDE;
 
 protected:
     nsWindow* mParent;
     bool mVisible;
     nsIntRegion mDirtyRegion;
     InputContext mInputContext;
-    nsCOMPtr<nsIdleService> mIdleService;
+    nsCOMPtr<nsIIdleServiceInternal> mIdleService;
 
     void BringToTop();
 
     // Call this function when the users activity is the direct cause of an
     // event (like a keypress or mouse click).
     void UserActivity();
 };
 
--- a/widget/gtk2/nsIdleServiceGTK.cpp
+++ b/widget/gtk2/nsIdleServiceGTK.cpp
@@ -24,17 +24,17 @@ typedef XScreenSaverInfo* (*_XScreenSave
 typedef void (*_XScreenSaverQueryInfo_fn)(Display* dpy, Drawable drw,
                                           XScreenSaverInfo *info);
 
 static bool sInitialized = false;
 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()
 {
     // This will leak - See comments in ~nsIdleServiceGTK().
     PRLibrary* xsslib = PR_LoadLibrary("libXss.so.1");
     if (!xsslib) // ouch.
     {
 #ifdef PR_LOGGING
--- a/widget/gtk2/nsIdleServiceGTK.h
+++ b/widget/gtk2/nsIdleServiceGTK.h
@@ -20,22 +20,34 @@ typedef struct {
     unsigned long til_or_since; // milliseconds since/til screensaver kicks in
     unsigned long idle;         // milliseconds idle
     unsigned long event_mask;   // event stuff
 } XScreenSaverInfo;
 
 class nsIdleServiceGTK : public nsIdleService
 {
 public:
-    NS_DECL_ISUPPORTS
-    nsIdleServiceGTK();
+    NS_DECL_ISUPPORTS_INHERITED
 
     bool PollIdleTime(PRUint32* aIdleTime);
 
+    static already_AddRefed<nsIdleServiceGTK> GetInstance()
+    {
+        nsIdleServiceGTK* idleService =
+            static_cast<nsIdleServiceGTK*>(nsIdleService::GetInstance().get());
+        if (!idleService) {
+            idleService = new nsIdleServiceGTK();
+            NS_ADDREF(idleService);
+        }
+
+        return idleService;
+    }
+
 private:
     ~nsIdleServiceGTK();
     XScreenSaverInfo* mXssInfo;
 
 protected:
+    nsIdleServiceGTK();
     bool UsePollMode();
 };
 
 #endif // nsIdleServiceGTK_h__
--- a/widget/gtk2/nsWidgetFactory.cpp
+++ b/widget/gtk2/nsWidgetFactory.cpp
@@ -61,16 +61,17 @@ static NS_DEFINE_CID(kXULFilePickerCID, 
 static NS_DEFINE_CID(kNativeFilePickerCID, NS_FILEPICKER_CID);
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsWindow)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsChildWindow)
 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)
 #endif
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsSound)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsScreenManagerGtk)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsImageToPixbuf)
 
@@ -104,17 +105,16 @@ nsNativeThemeGTKConstructor(nsISupports 
     rv = inst->QueryInterface(aIID, aResult);
     NS_RELEASE(inst);
 
     return rv;
 }
 #endif
 
 #if defined(MOZ_X11)
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsIdleServiceGTK)
 namespace mozilla {
 namespace widget {
 // This constructor should really be shared with all platforms.
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(GfxInfo, Init)
 }
 }
 #endif
 
--- a/widget/gtk2/nsWindow.cpp
+++ b/widget/gtk2/nsWindow.cpp
@@ -64,17 +64,17 @@
 #include "mozilla/Preferences.h"
 #include "nsIPrefService.h"
 #include "nsIGConfService.h"
 #include "nsIServiceManager.h"
 #include "nsIStringBundle.h"
 #include "nsGfxCIID.h"
 #include "nsIObserverService.h"
 
-#include "nsIdleService.h"
+#include "nsIIdleServiceInternal.h"
 #include "nsIPropertyBag2.h"
 
 #ifdef ACCESSIBILITY
 #include "nsAccessibilityService.h"
 #include "nsIAccessibleDocument.h"
 
 using namespace mozilla;
 using namespace mozilla::widget;
@@ -317,20 +317,20 @@ static inline bool TimestampIsNewerThan(
     // Timestamps are just the least significant bits of a monotonically
     // increasing function, and so the use of unsigned overflow arithmetic.
     return a - b <= G_MAXUINT32/2;
 }
 
 static void
 UpdateLastInputEventTime(void *aGdkEvent)
 {
-    nsCOMPtr<nsIdleService> idleService =
+    nsCOMPtr<nsIIdleServiceInternal> idleService =
         do_GetService("@mozilla.org/widget/idleservice;1");
     if (idleService) {
-        idleService->ResetIdleTimeOut();
+        idleService->ResetIdleTimeOut(0);
     }
 
     guint timestamp = gdk_event_get_time(static_cast<GdkEvent*>(aGdkEvent));
     if (timestamp == GDK_CURRENT_TIME)
         return;
 
     sLastUserInputTime = timestamp;
 }
--- a/widget/nsGUIEvent.h
+++ b/widget/nsGUIEvent.h
@@ -213,16 +213,20 @@ class nsHashKey;
 #define NS_PLUGIN_FOCUS                  (NS_WINDOW_START + 63)
 
 #define NS_OFFLINE                       (NS_WINDOW_START + 64)
 #define NS_ONLINE                        (NS_WINDOW_START + 65)
 
 // 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)
 #define NS_MOUSE_BUTTON_DOWN            (NS_MOUSE_MESSAGE_START + 2)
 #define NS_MOUSE_ENTER                  (NS_MOUSE_MESSAGE_START + 22)
 #define NS_MOUSE_EXIT                   (NS_MOUSE_MESSAGE_START + 23)
 #define NS_MOUSE_DOUBLECLICK            (NS_MOUSE_MESSAGE_START + 24)
 #define NS_MOUSE_CLICK                  (NS_MOUSE_MESSAGE_START + 27)
--- a/widget/nsIIdleService.idl
+++ b/widget/nsIIdleService.idl
@@ -61,8 +61,18 @@ interface nsIIdleService : nsISupports
      * @note
      * Removing an observer will remove it once, for the idle time you specify. 
      * If you have added an observer multiple times, you will need to remove it
      * just as many times.
      */
     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"
+%}
new file mode 100644
--- /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);
+};
+
--- a/widget/os2/nsIdleServiceOS2.cpp
+++ b/widget/os2/nsIdleServiceOS2.cpp
@@ -5,17 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsIdleServiceOS2.h"
 
 // prototype for funtion imported from DSSaver
 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)
 {
   const char error[256] = "";
   if (DosLoadModule(error, 256, "SSCORE", &mHMod) == NO_ERROR) {
     if (DosQueryProcAddr(mHMod, 0, "SSCore_GetInactivityTime",
                          (PFN*)&DSSaver_GetInactivityTime) == NO_ERROR) {
--- a/widget/os2/nsIdleServiceOS2.h
+++ b/widget/os2/nsIdleServiceOS2.h
@@ -10,25 +10,36 @@
 #include "nsIdleService.h"
 #define INCL_DOSMODULEMGR
 #define INCL_DOSERRORS
 #include <os2.h>
 
 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<nsIdleServiceOS2> GetInstance()
+  {
+    nsIdleServiceOS2* idleService =
+      static_cast<nsIdleServiceOS2*>(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();
 };
 
 #endif // nsIdleServiceOS2_h__
--- a/widget/os2/nsWidgetFactory.cpp
+++ b/widget/os2/nsWidgetFactory.cpp
@@ -65,17 +65,18 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsTransfe
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsHTMLFormatConverter)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsDragService)
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsDeviceContextSpecOS2)
 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);
 NS_DEFINE_NAMED_CID(NS_BIDIKEYBOARD_CID);
 NS_DEFINE_NAMED_CID(NS_CHILD_CID);
 NS_DEFINE_NAMED_CID(NS_CLIPBOARD_CID);
 NS_DEFINE_NAMED_CID(NS_CLIPBOARDHELPER_CID);
 NS_DEFINE_NAMED_CID(NS_DRAGSERVICE_CID);
--- a/widget/qt/nsIdleServiceQt.cpp
+++ b/widget/qt/nsIdleServiceQt.cpp
@@ -24,17 +24,17 @@ typedef void (*_XScreenSaverQueryInfo_fn
 
 static _XScreenSaverQueryExtension_fn _XSSQueryExtension = nsnull;
 static _XScreenSaverAllocInfo_fn _XSSAllocInfo = nsnull;
 static _XScreenSaverQueryInfo_fn _XSSQueryInfo = nsnull;
 #endif
 
 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)
     : mXssInfo(nsnull)
 #endif
 {
 }
 
--- a/widget/qt/nsIdleServiceQt.h
+++ b/widget/qt/nsIdleServiceQt.h
@@ -22,24 +22,36 @@ typedef struct {
     unsigned long idle;         // milliseconds idle
     unsigned long event_mask;   // event stuff
 } XScreenSaverInfo;
 #endif
 
 class nsIdleServiceQt : public nsIdleService
 {
 public:
-    NS_DECL_ISUPPORTS
-    nsIdleServiceQt();
+    NS_DECL_ISUPPORTS_INHERITED
 
     bool PollIdleTime(PRUint32* aIdleTime);
 
+    static already_AddRefed<nsIdleServiceQt> GetInstance()
+    {
+        nsIdleServiceQt* idleService =
+            static_cast<nsIdleServiceQt*>(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();
 };
 
 #endif // nsIdleServiceQt_h__
--- a/widget/qt/nsWidgetFactory.cpp
+++ b/widget/qt/nsWidgetFactory.cpp
@@ -45,17 +45,17 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsChildWi
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsPopupWindow)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsHTMLFormatConverter)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsTransferable)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsScreenManagerQt)
 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
 nsFilePickerConstructor(nsISupports *aOuter, REFNSIID aIID,
                         void **aResult)
 {
   *aResult = nsnull;
   if (aOuter != nsnull) {
--- a/widget/qt/nsWindow.cpp
+++ b/widget/qt/nsWindow.cpp
@@ -3295,17 +3295,17 @@ nsWindow::SetSoftwareKeyboardState(bool 
 void
 nsWindow::UserActivity()
 {
   if (!mIdleService) {
     mIdleService = do_GetService("@mozilla.org/widget/idleservice;1");
   }
 
   if (mIdleService) {
-    mIdleService->ResetIdleTimeOut();
+    mIdleService->ResetIdleTimeOut(0);
   }
 }
 
 PRUint32
 nsWindow::GetGLFrameBufferFormat()
 {
     if (mLayerManager &&
         mLayerManager->GetBackendType() == LayerManager::LAYERS_OPENGL) {
--- a/widget/qt/nsWindow.h
+++ b/widget/qt/nsWindow.h
@@ -16,16 +16,17 @@
 #include "nsAutoPtr.h"
 
 #include "nsBaseWidget.h"
 #include "nsGUIEvent.h"
 
 #include "nsWeakReference.h"
 
 #include "nsGkAtoms.h"
+#include "nsIIdleServiceInternal.h"
 
 #ifdef MOZ_LOGGING
 
 // make sure that logging is enabled before including prlog.h
 #define FORCE_PR_LOG
 
 #include "prlog.h"
 #include "nsTArray.h"
@@ -319,17 +320,17 @@ private:
     MozQWidget*        mWidget;
 
     PRUint32           mIsVisible : 1,
                        mActivatePending : 1;
     PRInt32            mSizeState;
     PluginType         mPluginType;
 
     nsRefPtr<gfxASurface> mThebesSurface;
-    nsCOMPtr<nsIdleService> mIdleService;
+    nsCOMPtr<nsIIdleServiceInternal> mIdleService;
 
     bool         mIsTransparent;
  
     // all of our DND stuff
     // this is the last window that had a drag event happen on it.
     void   InitDragEvent         (nsMouseEvent &aEvent);
 
     // this is everything we need to be able to fire motion events
--- a/widget/windows/nsIdleServiceWin.cpp
+++ b/widget/windows/nsIdleServiceWin.cpp
@@ -3,17 +3,17 @@
  */
 /* 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 "nsIdleServiceWin.h"
 #include <windows.h>
 
-NS_IMPL_ISUPPORTS2(nsIdleServiceWin, nsIIdleService, nsIdleService)
+NS_IMPL_ISUPPORTS_INHERITED0(nsIdleServiceWin, nsIdleService)
 
 bool
 nsIdleServiceWin::PollIdleTime(PRUint32 *aIdleTime)
 {
     LASTINPUTINFO inputInfo;
     inputInfo.cbSize = sizeof(inputInfo);
     if (!::GetLastInputInfo(&inputInfo))
         return false;
--- a/widget/windows/nsIdleServiceWin.h
+++ b/widget/windows/nsIdleServiceWin.h
@@ -17,17 +17,31 @@
 #ifndef SAFE_COMPARE_EVEN_WITH_WRAPPING
 #define SAFE_COMPARE_EVEN_WITH_WRAPPING(A, B) (((int)((long)A - (long)B) & 0xFFFFFFFF))
 #endif
 
 
 class nsIdleServiceWin : public nsIdleService
 {
 public:
-    NS_DECL_ISUPPORTS
+    NS_DECL_ISUPPORTS_INHERITED
 
     bool PollIdleTime(PRUint32* aIdleTime);
 
+    static already_AddRefed<nsIdleServiceWin> GetInstance()
+    {
+        nsIdleServiceWin* idleService =
+            static_cast<nsIdleServiceWin*>(nsIdleService::GetInstance().get());
+        if (!idleService) {
+            idleService = new nsIdleServiceWin();
+            NS_ADDREF(idleService);
+        }
+        
+        return idleService;
+    }
+
 protected:
+    nsIdleServiceWin() { }
+    virtual ~nsIdleServiceWin() { }
     bool UsePollMode();
 };
 
 #endif // nsIdleServiceWin_h__
--- a/widget/windows/nsWidgetFactory.cpp
+++ b/widget/windows/nsWidgetFactory.cpp
@@ -44,17 +44,17 @@
 #endif
 
 using namespace mozilla::widget;
 
 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)
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(WinTaskbar)
 NS_GENERIC_FACTORY_CONSTRUCTOR(JumpListBuilder)
 NS_GENERIC_FACTORY_CONSTRUCTOR(JumpListItem)
 NS_GENERIC_FACTORY_CONSTRUCTOR(JumpListSeparator)
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -6126,17 +6126,17 @@ void nsWindow::UserActivity()
 {
   // Check if we have the idle service, if not we try to get it.
   if (!mIdleService) {
     mIdleService = do_GetService("@mozilla.org/widget/idleservice;1");
   }
 
   // Check that we now have the idle service.
   if (mIdleService) {
-    mIdleService->ResetIdleTimeOut();
+    mIdleService->ResetIdleTimeOut(0);
   }
 }
 
 bool nsWindow::OnTouch(WPARAM wParam, LPARAM lParam)
 {
   PRUint32 cInputs = LOWORD(wParam);
   PTOUCHINPUT pInputs = new TOUCHINPUT[cInputs];
 
--- a/widget/windows/nsWindow.h
+++ b/widget/windows/nsWindow.h
@@ -36,16 +36,18 @@
 #include "OLEACC.H"
 #include "mozilla/a11y/Accessible.h"
 #endif
 
 #include "nsUXThemeData.h"
 
 #include "nsIDOMMouseEvent.h"
 
+#include "nsIIdleServiceInternal.h"
+
 /**
  * Forward class definitions
  */
 
 class nsNativeDragTarget;
 class nsIRollupListener;
 class nsIFile;
 class imgIContainer;
@@ -509,17 +511,17 @@ protected:
   // Indicates custom frames are enabled
   bool                  mCustomNonClient;
   // Cached copy of L&F's resize border  
   PRInt32               mHorResizeMargin;
   PRInt32               mVertResizeMargin;
   // Height of the caption plus border
   PRInt32               mCaptionHeight;
 
-  nsCOMPtr<nsIdleService> mIdleService;
+  nsCOMPtr<nsIIdleServiceInternal> mIdleService;
 
   // Hook Data Memebers for Dropdowns. sProcessHook Tells the
   // hook methods whether they should be processing the hook
   // messages.
   static HHOOK          sMsgFilterHook;
   static HHOOK          sCallProcHook;
   static HHOOK          sCallMouseHook;
   static bool           sProcessHook;
--- a/widget/xpwidgets/nsIdleService.cpp
+++ b/widget/xpwidgets/nsIdleService.cpp
@@ -18,20 +18,16 @@
 #include "mozilla/Telemetry.h"
 
 #ifdef ANDROID
 #include <android/log.h>
 #endif
 
 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 */
 
 // Time used by the daily idle serivce to determine a significant idle time.
 #define DAILY_SIGNIFICANT_IDLE_SERVICE_SEC 300 /* 5 min */
 // Pref for last time (seconds since epoch) daily notification was sent.
 #define PREF_LAST_DAILY "idle.lastDailyNotification"
 // Number of seconds in a day.
@@ -68,17 +64,17 @@ nsIdleServiceDaily::Observe(nsISupports 
     return NS_OK;
   }
 
   if (strcmp(aTopic, "xpcom-will-shutdown") == 0 ||
       strcmp(aTopic, "profile-change-teardown") == 0) {
     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);
 
 #ifdef ANDROID
   __android_log_print(ANDROID_LOG_INFO, "IdleService", "Notifying idle-daily observers");
 #endif
 
@@ -259,36 +255,55 @@ nsIdleServiceDaily::DailyCallback(nsITim
  * the current timeout.
  *
  */
 
 
 ////////////////////////////////////////////////////////////////////////////////
 //// nsIdleService
 
+namespace { 
+nsIdleService* gIdleService;
+}
+
+already_AddRefed<nsIdleService>
+nsIdleService::GetInstance()
+{
+  nsRefPtr<nsIdleService> instance(gIdleService);
+  return instance.forget();
+}
+
 nsIdleService::nsIdleService() : mCurrentlySetToTimeoutAtInPR(0),
                                  mAnyObserverIdle(false),
                                  mDeltaToNextIdleSwitchInS(PR_UINT32_MAX),
                                  mLastUserInteractionInPR(PR_Now())
 {
 #ifdef PR_LOGGING
   if (sLog == NULL)
     sLog = PR_NewLogModule("idleService");
 #endif
+  MOZ_ASSERT(!gIdleService);
+  gIdleService = this;
   mDailyIdle = new nsIdleServiceDaily(this);
   mDailyIdle->Init();
 }
 
 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)
 {
   PR_LOG(sLog, PR_LOG_DEBUG,
          ("idleService: Register idle observer %x for %d seconds",
           aObserver, aIdleTimeInS));
 #ifdef ANDROID
   __android_log_print(ANDROID_LOG_INFO, "IdleService", "Register idle observer %x for %d seconds",
@@ -368,31 +383,31 @@ nsIdleService::RemoveIdleObserver(nsIObs
 #ifdef ANDROID
   __android_log_print(ANDROID_LOG_INFO, "IdleService",
                       "Failed to remove idle observer %x (%d seconds)",
                       aObserver, aTimeInS);
 #endif
   return NS_ERROR_FAILURE;
 }
 
-void
+NS_IMETHODIMP
 nsIdleService::ResetIdleTimeOut(PRUint32 idleDeltaInMS)
 {
   PR_LOG(sLog, PR_LOG_DEBUG,
          ("idleService: Reset idle timeout (last interaction %u msec)",
           idleDeltaInMS));
 
   // Store the time
   mLastUserInteractionInPR = PR_Now() - (idleDeltaInMS * PR_USEC_PER_MSEC);
 
   // If no one is idle, then we are done, any existing timers can keep running.
   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.
   Telemetry::AutoTimer<Telemetry::IDLE_NOTIFY_BACK_MS> timer;
   nsCOMArray<nsIObserver> notifyList;
   mDeltaToNextIdleSwitchInS = PR_UINT32_MAX;
 
   // Loop through all listeners, and find any that have detected idle.
@@ -417,17 +432,17 @@ nsIdleService::ResetIdleTimeOut(PRUint32
   ReconfigureTimer();
 
   PRInt32 numberOfPendingNotifications = notifyList.Count();
   Telemetry::Accumulate(Telemetry::IDLE_NOTIFY_BACK_LISTENERS,
                         numberOfPendingNotifications);
 
   // 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
   // they will be reawaken by the timer that is already running.
 
   // We need a text string to send with any state change events.
   nsAutoString timeStr;
 
@@ -439,20 +454,20 @@ nsIdleService::ResetIdleTimeOut(PRUint32
            ("idleService: Reset idle timeout: tell observer %x user is back",
             notifyList[numberOfPendingNotifications]));
 #ifdef ANDROID
     __android_log_print(ANDROID_LOG_INFO, "IdleService",
                         "Reset idle timeout: tell observer %x user is back",
                         notifyList[numberOfPendingNotifications]);
 #endif
     notifyList[numberOfPendingNotifications]->Observe(this,
-                                                      OBSERVER_TOPIC_BACK,
+                                                      OBSERVER_TOPIC_ACTIVE,
                                                       timeStr.get());
   }
-
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsIdleService::GetIdleTime(PRUint32* idleTime)
 {
   // Check sanity of in parameter.
   if (!idleTime) {
     return NS_ERROR_NULL_POINTER;
--- a/widget/xpwidgets/nsIdleService.h
+++ b/widget/xpwidgets/nsIdleService.h
@@ -3,17 +3,17 @@
  */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsIdleService_h__
 #define nsIdleService_h__
 
-#include "nsIIdleService.h"
+#include "nsIIdleServiceInternal.h"
 #include "nsCOMPtr.h"
 #include "nsITimer.h"
 #include "nsTArray.h"
 #include "nsIObserver.h"
 #include "nsIIdleService.h"
 #include "nsCategoryCache.h"
 #include "nsWeakReference.h"
 
@@ -86,36 +86,27 @@ private:
   nsCategoryCache<nsIObserver> mCategoryObservers;
 
   /**
    * Boolean set to true when daily idle notifications should be disabled.
    */
   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<nsIdleService> GetInstance();
+
+  nsIdleService();
   virtual ~nsIdleService();
 
   /**
    * If there is a platform specific function to poll the system idel time
    * then that must be returned in this function, and the function MUST return
    * true, otherwise then the function should be left unimplemented or made
    * to return false (this can also be used for systems where it depends on
    * the configuration of the system if the idle time can be determined)
--- a/xpcom/glue/nsTObserverArray.h
+++ b/xpcom/glue/nsTObserverArray.h
@@ -140,24 +140,49 @@ class nsAutoTObserverArray : protected n
     template<class Item>
     index_type IndexOf(const Item& item, index_type start = 0) const {
       return mArray.IndexOf(item, start);
     }
 
     //
     // 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<class Item>
+    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.
     // @param item   The item to prepend.
     // @return       true if the element was found, or inserted successfully.
     template<class Item>
     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.
     // @param item   The item to append.
     // @return A pointer to the newly appended element, or null on OOM.
     template<class Item>
     elem_type* AppendElement(const Item& item) {
       return mArray.AppendElement(item);