Bug 714358: Time manager implementation. r=jlebar
☠☠ backed out by e001a7b3b817 ☠ ☠
authorSteven Lee <slee@mozilla.com>
Tue, 07 Aug 2012 19:11:00 -0400
changeset 103683 5439489dc320dbe10c3826682983480b712039ae
parent 103682 983f76488e59d6cda341d0441c2666acf4611aa1
child 103684 6be6808c2a4a291d700d8e83ae570d7693b1dfab
push id37
push usershu@rfrn.org
push dateThu, 16 Aug 2012 01:15:22 +0000
reviewersjlebar
bugs714358
milestone17.0a1
Bug 714358: Time manager implementation. r=jlebar
b2g/installer/package-manifest.in
content/base/src/nsGkAtomList.h
content/events/public/nsEventNameList.h
content/events/src/nsDOMEvent.cpp
content/events/src/nsDOMEvent.h
content/events/src/nsEventListenerManager.cpp
content/events/src/nsEventListenerManager.h
dom/base/Navigator.cpp
dom/base/Navigator.h
dom/base/nsDOMClassInfo.cpp
dom/base/nsDOMClassInfoClasses.h
dom/base/nsGlobalWindow.cpp
dom/base/nsGlobalWindow.h
dom/base/nsPIDOMWindow.h
dom/time/Makefile.in
dom/time/TimeChangeObserver.cpp
dom/time/TimeChangeObserver.h
dom/time/TimeManager.cpp
dom/time/TimeManager.h
dom/time/nsIDOMTimeManager.idl
layout/build/Makefile.in
widget/nsGUIEvent.h
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -196,16 +196,17 @@
 @BINPATH@/components/dom_stylesheets.xpt
 @BINPATH@/components/dom_threads.xpt
 @BINPATH@/components/dom_traversal.xpt
 @BINPATH@/components/dom_views.xpt
 @BINPATH@/components/dom_xbl.xpt
 @BINPATH@/components/dom_xpath.xpt
 @BINPATH@/components/dom_xul.xpt
 @BINPATH@/components/dom_loadsave.xpt
+@BINPATH@/components/dom_time.xpt
 @BINPATH@/components/downloads.xpt
 @BINPATH@/components/editor.xpt
 @BINPATH@/components/embed_base.xpt
 @BINPATH@/components/extensions.xpt
 @BINPATH@/components/exthandler.xpt
 @BINPATH@/components/exthelper.xpt
 @BINPATH@/components/fastfind.xpt
 @BINPATH@/components/feeds.xpt
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -678,16 +678,17 @@ GK_ATOM(onmouseout, "onmouseout")
 GK_ATOM(onmouseover, "onmouseover")
 GK_ATOM(onMozMouseHittest, "onMozMouseHittest")
 GK_ATOM(onmouseup, "onmouseup")
 GK_ATOM(onMozAfterPaint, "onMozAfterPaint")
 GK_ATOM(onmozfullscreenchange, "onmozfullscreenchange")
 GK_ATOM(onmozfullscreenerror, "onmozfullscreenerror")
 GK_ATOM(onmozpointerlockchange, "onmozpointerlockchange")
 GK_ATOM(onmozpointerlockerror, "onmozpointerlockerror")
+GK_ATOM(onmoztimechange, "onmoztimechange")
 GK_ATOM(onMozMousePixelScroll, "onMozMousePixelScroll")
 GK_ATOM(onMozScrolledAreaChanged, "onMozScrolledAreaChanged")
 GK_ATOM(ononline, "ononline")
 GK_ATOM(onoffline, "onoffline")
 GK_ATOM(onopen, "onopen")
 GK_ATOM(onoverflow, "onoverflow")
 GK_ATOM(onoverflowchanged, "onoverflowchanged")
 GK_ATOM(onpagehide, "onpagehide")
--- a/content/events/public/nsEventNameList.h
+++ b/content/events/public/nsEventNameList.h
@@ -430,16 +430,20 @@ WINDOW_ONLY_EVENT(deviceproximity,
 WINDOW_ONLY_EVENT(userproximity,
                   NS_USER_PROXIMITY,
                   EventNameType_None,
                   NS_EVENT)
 WINDOW_ONLY_EVENT(devicelight,
                   NS_DEVICE_LIGHT,
                   EventNameType_None,
                   NS_EVENT)
+WINDOW_ONLY_EVENT(moztimechange,
+                  NS_MOZ_TIME_CHANGE_EVENT,
+                  EventNameType_None,
+                  NS_EVENT)
 
 TOUCH_EVENT(touchstart,
             NS_TOUCH_START,
             EventNameType_All,
             NS_TOUCH_EVENT)
 TOUCH_EVENT(touchend,
             NS_TOUCH_END,
             EventNameType_All,
--- a/content/events/src/nsDOMEvent.cpp
+++ b/content/events/src/nsDOMEvent.cpp
@@ -92,17 +92,18 @@ static const char* const sEventNames[] =
   "transitionend",
   "animationstart",
   "animationend",
   "animationiteration",
   "devicemotion",
   "deviceorientation",
   "deviceproximity",
   "userproximity",
-  "devicelight"
+  "devicelight",
+  "moztimechange"
 };
 
 static char *sPopupAllowedEvents;
 
 
 nsDOMEvent::nsDOMEvent(nsPresContext* aPresContext, nsEvent* aEvent)
 {
   mPrivateDataDuplicated = false;
--- a/content/events/src/nsDOMEvent.h
+++ b/content/events/src/nsDOMEvent.h
@@ -172,17 +172,18 @@ public:
     eDOMEvents_transitionend,
     eDOMEvents_animationstart,
     eDOMEvents_animationend,
     eDOMEvents_animationiteration,
     eDOMEvents_devicemotion,
     eDOMEvents_deviceorientation,
     eDOMEvents_deviceproximity,
     eDOMEvents_userproximity,
-    eDOMEvents_devicelight
+    eDOMEvents_devicelight,
+    eDOMEvents_moztimechange
   };
 
   nsDOMEvent(nsPresContext* aPresContext, nsEvent* aEvent);
   virtual ~nsDOMEvent();
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsDOMEvent, nsIDOMEvent)
 
--- a/content/events/src/nsEventListenerManager.cpp
+++ b/content/events/src/nsEventListenerManager.cpp
@@ -48,16 +48,17 @@
 #include "nsDOMJSUtils.h"
 #include "nsDOMScriptObjectHolder.h"
 #include "nsDataHashtable.h"
 #include "nsCOMArray.h"
 #include "nsEventListenerService.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsJSEnvironment.h"
 #include "xpcpublic.h"
+#include "TimeChangeObserver.h"
 
 using namespace mozilla::dom;
 using namespace mozilla::hal;
 
 #define EVENT_TYPE_EQUALS( ls, type, userType ) \
   (ls->mEventType == type && \
   (ls->mEventType != NS_USER_DEFINED_EVENT || ls->mTypeAtom == userType))
 
@@ -266,16 +267,18 @@ nsEventListenerManager::AddEventListener
   } else if (aTypeAtom == nsGkAtoms::ondeviceorientation) {
     EnableDevice(NS_DEVICE_ORIENTATION);
   } else if (aTypeAtom == nsGkAtoms::ondeviceproximity || aTypeAtom == nsGkAtoms::onuserproximity) {
     EnableDevice(NS_DEVICE_PROXIMITY);
   } else if (aTypeAtom == nsGkAtoms::ondevicelight) {
     EnableDevice(NS_DEVICE_LIGHT);
   } else if (aTypeAtom == nsGkAtoms::ondevicemotion) {
     EnableDevice(NS_DEVICE_MOTION);
+  } else if (aTypeAtom == nsGkAtoms::onmoztimechange) {
+    EnableTimeChangeNotifications();
   } else if ((aType >= NS_MOZTOUCH_DOWN && aType <= NS_MOZTOUCH_UP) ||
              (aTypeAtom == nsGkAtoms::ontouchstart ||
               aTypeAtom == nsGkAtoms::ontouchend ||
               aTypeAtom == nsGkAtoms::ontouchmove ||
               aTypeAtom == nsGkAtoms::ontouchenter ||
               aTypeAtom == nsGkAtoms::ontouchleave ||
               aTypeAtom == nsGkAtoms::ontouchcancel)) {
     mMayHaveTouchEventListener = true;
@@ -391,39 +394,42 @@ nsEventListenerManager::RemoveEventListe
   }
 
   nsListenerStruct* ls;
   aFlags &= ~NS_PRIV_EVENT_UNTRUSTED_PERMITTED;
 
   PRUint32 count = mListeners.Length();
   PRUint32 typeCount = 0;
   bool deviceType = IsDeviceType(aType);
+  bool timeChangeEvent = (aType == NS_MOZ_TIME_CHANGE_EVENT);
 
   for (PRUint32 i = 0; i < count; ++i) {
     ls = &mListeners.ElementAt(i);
     if (EVENT_TYPE_EQUALS(ls, aType, aUserType)) {
       ++typeCount;
       if (ls->mListener == aListener &&
           (ls->mFlags & ~NS_PRIV_EVENT_UNTRUSTED_PERMITTED) == aFlags) {
         nsRefPtr<nsEventListenerManager> kungFuDeathGrip = this;
         mListeners.RemoveElementAt(i);
         --count;
         mNoListenerForEvent = NS_EVENT_TYPE_NULL;
         mNoListenerForEventAtom = nullptr;
 
-        if (!deviceType) {
+        if (!deviceType && !timeChangeEvent) {
           return;
         }
         --typeCount;
       }
     }
   }
 
   if (deviceType && typeCount == 0) {
     DisableDevice(aType);
+  } else if (timeChangeEvent && typeCount == 0) {
+    DisableTimeChangeNotifications();
   }
 }
 
 static inline bool
 ListenerCanHandle(nsListenerStruct* aLs, nsEvent* aEvent)
 {
   // This is slightly different from EVENT_TYPE_EQUALS in that it returns
   // true even when aEvent->message == NS_USER_DEFINED_EVENT and
@@ -1104,8 +1110,32 @@ nsEventListenerManager::UnmarkGrayJSList
     if (jsl) {
       xpc_UnmarkGrayObject(jsl->GetHandler());
       xpc_UnmarkGrayObject(jsl->GetEventScope());
     } else if (ls.mListenerType == eWrappedJSListener) {
       xpc_TryUnmarkWrappedGrayObject(ls.mListener);
     }
   }
 }
+
+void
+nsEventListenerManager::EnableTimeChangeNotifications()
+{
+  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mTarget);
+  if (!window) {
+    return;
+  }
+
+  NS_ASSERTION(window->IsInnerWindow(), "Target should not be an outer window");
+  window->EnableTimeChangeNotifications();
+}
+
+void
+nsEventListenerManager::DisableTimeChangeNotifications()
+{
+  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mTarget);
+  if (!window) {
+    return;
+  }
+
+  NS_ASSERTION(window->IsInnerWindow(), "Target should not be an outer window");
+  window->DisableTimeChangeNotifications();
+}
--- a/content/events/src/nsEventListenerManager.h
+++ b/content/events/src/nsEventListenerManager.h
@@ -256,16 +256,19 @@ protected:
                               JSObject *aHandler,
                               bool aPermitUntrustedEvents,
                               nsListenerStruct **aListenerStruct);
 
   bool IsDeviceType(PRUint32 aType);
   void EnableDevice(PRUint32 aType);
   void DisableDevice(PRUint32 aType);
 
+  void EnableTimeChangeNotifications();
+  void DisableTimeChangeNotifications();
+
 public:
   /**
    * Set the "inline" event listener for aEventName to |v|.  This
    * might actually remove the event listener, depending on the value
    * of |v|.  Note that on entry to this function cx and aScope might
    * not be in the same compartment, though cx and v are guaranteed to
    * be in the same compartment.
    */
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -33,16 +33,17 @@
 #include "nsISmsService.h"
 #include "mozilla/Hal.h"
 #include "nsIWebNavigation.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/StaticPtr.h"
 #include "Connection.h"
 #include "MobileConnection.h"
 #include "nsIIdleObserver.h"
+#include "TimeManager.h"
 
 #ifdef MOZ_MEDIA_NAVIGATOR
 #include "MediaManager.h"
 #endif
 #ifdef MOZ_B2G_RIL
 #include "TelephonyFactory.h"
 #endif
 #ifdef MOZ_B2G_BT
@@ -110,16 +111,17 @@ NS_INTERFACE_MAP_BEGIN(Navigator)
   NS_INTERFACE_MAP_ENTRY(nsIDOMNavigatorTelephony)
 #endif
   NS_INTERFACE_MAP_ENTRY(nsIDOMMozNavigatorNetwork)
 #ifdef MOZ_B2G_BT
   NS_INTERFACE_MAP_ENTRY(nsIDOMNavigatorBluetooth)
 #endif
   NS_INTERFACE_MAP_ENTRY(nsIDOMNavigatorCamera)
   NS_INTERFACE_MAP_ENTRY(nsIDOMNavigatorSystemMessages)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMMozNavigatorTime)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Navigator)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_ADDREF(Navigator)
 NS_IMPL_RELEASE(Navigator)
 
 void
 Navigator::Invalidate()
@@ -192,16 +194,19 @@ Navigator::Invalidate()
 #endif
 
   PRUint32 len = mDeviceStorageStores.Length();
   for (PRUint32 i = 0; i < len; ++i) {
     mDeviceStorageStores[i]->Shutdown();
   }
   mDeviceStorageStores.Clear();
 
+  if (mTimeManager) {
+    mTimeManager = nsnull;
+  }
 }
 
 nsPIDOMWindow *
 Navigator::GetWindow()
 {
   nsCOMPtr<nsPIDOMWindow> win(do_QueryReferent(mWindow));
 
   return win;
@@ -1236,31 +1241,19 @@ Navigator::GetMozConnection(nsIDOMMozCon
 
 NS_IMETHODIMP
 Navigator::GetMozMobileConnection(nsIDOMMozMobileConnection** aMobileConnection)
 {
   *aMobileConnection = nullptr;
 
   if (!mMobileConnection) {
     nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
-    NS_ENSURE_TRUE(window && window->GetDocShell(), NS_OK);
-
-    // Chrome is always allowed access, so do the permission check only
-    // for non-chrome pages.
-    if (!nsContentUtils::IsCallerChrome()) {
-      nsCOMPtr<nsIDocument> doc = do_QueryInterface(window->GetExtantDocument());
-      NS_ENSURE_TRUE(doc, NS_OK);
-
-      nsCOMPtr<nsIURI> uri;
-      doc->NodePrincipal()->GetURI(getter_AddRefs(uri));
-
-      if (!nsContentUtils::URIIsChromeOrInPref(uri, "dom.mobileconnection.whitelist")) {
-        return NS_OK;
-      }
-    }
+    if (!CheckPermission("dom.mobileconnection.whitelist")) {
+      return NS_OK;
+     }
 
     mMobileConnection = new network::MobileConnection();
     mMobileConnection->Init(window);
   }
 
   NS_ADDREF(*aMobileConnection = mMobileConnection);
   return NS_OK;
 }
@@ -1347,16 +1340,35 @@ Navigator::MozSetMessageHandler(const ns
 
   return mMessagesManager->MozSetMessageHandler(aType, aCallback);
 #else
   return NS_ERROR_NOT_IMPLEMENTED;
 #endif
 }
 
 //*****************************************************************************
+//    Navigator::nsIDOMNavigatorTime
+//*****************************************************************************
+NS_IMETHODIMP
+Navigator::GetMozTime(nsIDOMMozTimeManager** aTime)
+{
+  if (!CheckPermission("dom.time.whitelist")) {
+    return NS_ERROR_DOM_SECURITY_ERR;
+  }
+
+  if (!mTimeManager) {
+    *aTime = nsnull;
+    mTimeManager = new time::TimeManager();
+  }
+
+  NS_ADDREF(*aTime = mTimeManager);
+  return NS_OK;
+}
+
+//*****************************************************************************
 //    nsNavigator::nsIDOMNavigatorCamera
 //*****************************************************************************
 
 NS_IMETHODIMP
 Navigator::GetMozCameras(nsIDOMCameraManager** aCameraManager)
 {
   if (!mCameraManager) {
     nsCOMPtr<nsPIDOMWindow> win = do_QueryReferent(mWindow);
@@ -1409,16 +1421,31 @@ Navigator::OnNavigation()
   MediaManager *manager = MediaManager::Get();
   manager->OnNavigation(win->WindowID());
 #endif
   if (mCameraManager) {
     mCameraManager->OnNavigation(win->WindowID());
   }
 }
 
+bool
+Navigator::CheckPermission(const char* aPref)
+{
+  if (!nsContentUtils::IsCallerChrome()) {
+    nsCOMPtr<nsPIDOMWindow> win = do_QueryReferent(mWindow);
+    NS_ENSURE_TRUE(win, false);
+    nsCOMPtr<nsIDocument> doc = do_QueryInterface(win->GetExtantDocument());
+    NS_ENSURE_TRUE(doc, false);
+    nsCOMPtr<nsIURI> uri;
+    doc->NodePrincipal()->GetURI(getter_AddRefs(uri));
+    return nsContentUtils::URIIsChromeOrInPref(uri, aPref);
+  }
+
+  return true;
+}
 } // namespace dom
 } // namespace mozilla
 
 nsresult
 NS_GetNavigatorUserAgent(nsAString& aUserAgent)
 {
   nsresult rv;
 
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -10,16 +10,17 @@
 #include "nsIDOMNavigator.h"
 #include "nsIDOMNavigatorGeolocation.h"
 #include "nsIDOMNavigatorDeviceStorage.h"
 #include "nsIDOMNavigatorDesktopNotification.h"
 #include "nsIDOMClientInformation.h"
 #include "nsINavigatorBattery.h"
 #include "nsIDOMNavigatorSms.h"
 #include "nsIDOMNavigatorNetwork.h"
+#include "nsIDOMNavigatorTime.h"
 #include "nsAutoPtr.h"
 #include "nsWeakReference.h"
 #include "DeviceStorage.h"
 
 class nsPluginArray;
 class nsMimeTypeArray;
 class nsGeolocation;
 class nsDesktopNotificationCenter;
@@ -64,16 +65,20 @@ namespace network {
 class Connection;
 class MobileConnection;
 } // namespace Connection;
 
 namespace power {
 class PowerManager;
 } // namespace power
 
+namespace time {
+class TimeManager;
+} // namespace time
+
 class Navigator : public nsIDOMNavigator
                 , public nsIDOMClientInformation
                 , public nsIDOMNavigatorDeviceStorage
                 , public nsIDOMNavigatorGeolocation
                 , public nsIDOMNavigatorDesktopNotification
                 , public nsINavigatorBattery
                 , public nsIDOMMozNavigatorSms
 #ifdef MOZ_MEDIA_NAVIGATOR
@@ -83,16 +88,17 @@ class Navigator : public nsIDOMNavigator
                 , public nsIDOMNavigatorTelephony
 #endif
                 , public nsIDOMMozNavigatorNetwork
 #ifdef MOZ_B2G_BT
                 , public nsIDOMNavigatorBluetooth
 #endif
                 , public nsIDOMNavigatorCamera
                 , public nsIDOMNavigatorSystemMessages
+                , public nsIDOMMozNavigatorTime
 {
 public:
   Navigator(nsPIDOMWindow *aInnerWindow);
   virtual ~Navigator();
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIDOMNAVIGATOR
   NS_DECL_NSIDOMCLIENTINFORMATION
@@ -108,16 +114,17 @@ public:
   NS_DECL_NSIDOMNAVIGATORTELEPHONY
 #endif
   NS_DECL_NSIDOMMOZNAVIGATORNETWORK
 
 #ifdef MOZ_B2G_BT
   NS_DECL_NSIDOMNAVIGATORBLUETOOTH
 #endif
   NS_DECL_NSIDOMNAVIGATORSYSTEMMESSAGES
+  NS_DECL_NSIDOMMOZNAVIGATORTIME
 
   static void Init();
 
   void Invalidate();
   nsPIDOMWindow *GetWindow();
 
   void RefreshMIMEArray();
 
@@ -139,16 +146,17 @@ public:
   // Helper to initialize mMessagesManager.
   nsresult EnsureMessagesManager();
 #endif
   NS_DECL_NSIDOMNAVIGATORCAMERA
 
 private:
   bool IsSmsAllowed() const;
   bool IsSmsSupported() const;
+  bool CheckPermission(const char* aPref);
 
   nsRefPtr<nsMimeTypeArray> mMimeTypes;
   nsRefPtr<nsPluginArray> mPlugins;
   nsRefPtr<nsGeolocation> mGeolocation;
   nsRefPtr<nsDesktopNotificationCenter> mNotification;
   nsRefPtr<battery::BatteryManager> mBatteryManager;
   nsRefPtr<power::PowerManager> mPowerManager;
   nsRefPtr<sms::SmsManager> mSmsManager;
@@ -159,16 +167,17 @@ private:
   nsRefPtr<network::Connection> mConnection;
   nsRefPtr<network::MobileConnection> mMobileConnection;
 #ifdef MOZ_B2G_BT
   nsCOMPtr<nsIDOMBluetoothManager> mBluetooth;
 #endif
   nsRefPtr<nsDOMCameraManager> mCameraManager;
   nsCOMPtr<nsIDOMNavigatorSystemMessages> mMessagesManager;
   nsTArray<nsRefPtr<nsDOMDeviceStorage> > mDeviceStorageStores;
+  nsRefPtr<time::TimeManager> mTimeManager;
   nsWeakPtr mWindow;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 nsresult NS_GetNavigatorUserAgent(nsAString& aUserAgent);
 nsresult NS_GetNavigatorPlatform(nsAString& aPlatform);
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -518,16 +518,17 @@ using mozilla::dom::indexedDB::IDBWrappe
 #include "BluetoothDevice.h"
 #include "BluetoothDeviceEvent.h"
 #include "BluetoothPropertyEvent.h"
 #endif
 
 #include "nsIDOMNavigatorSystemMessages.h"
 
 #include "mozilla/dom/Activity.h"
+#include "TimeManager.h"
 
 #include "DOMCameraManager.h"
 #include "CameraControl.h"
 #include "CameraCapabilities.h"
 
 #include "DOMError.h"
 #include "DOMRequest.h"
 #include "nsIOpenWindowEventDetail.h"
@@ -1693,16 +1694,19 @@ static nsDOMClassInfoData sClassInfoData
   NS_DEFINE_CLASSINFO_DATA_WITH_NAME(DOMFileHandle, FileHandle, nsEventTargetSH,
                            EVENTTARGET_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(FileRequest, nsEventTargetSH,
                            EVENTTARGET_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(LockedFile, nsEventTargetSH,
                            EVENTTARGET_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(MozActivity, nsEventTargetSH,
                            EVENTTARGET_SCRIPTABLE_FLAGS)
+
+  NS_DEFINE_CLASSINFO_DATA(MozTimeManager, nsDOMGenericSH,
+                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
 };
 
 // Objects that should be constructable through |new Name();|
 struct nsContractIDMapData
 {
   PRInt32 mDOMClassInfoID;
   const char *mContractID;
 };
@@ -2466,16 +2470,17 @@ nsDOMClassInfo::Init()
 #endif
     DOM_CLASSINFO_MAP_CONDITIONAL_ENTRY(nsIDOMMozNavigatorNetwork,
                                         network::IsAPIEnabled())
 #ifdef MOZ_B2G_BT
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMNavigatorBluetooth)
 #endif
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMNavigatorCamera)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMNavigatorSystemMessages)
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozNavigatorTime)
 
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(Plugin, nsIDOMPlugin)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMPlugin)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(PluginArray, nsIDOMPluginArray)
@@ -4501,16 +4506,20 @@ nsDOMClassInfo::Init()
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(MozActivity, nsIDOMMozActivity)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozActivity)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMDOMRequest)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
   DOM_CLASSINFO_MAP_END
 
+  DOM_CLASSINFO_MAP_BEGIN(MozTimeManager, nsIDOMMozTimeManager)
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozTimeManager)
+  DOM_CLASSINFO_MAP_END
+
 #ifdef DEBUG
   {
     PRUint32 i = ArrayLength(sClassInfoData);
 
     if (i != eDOMClassInfoIDCount) {
       NS_ERROR("The number of items in sClassInfoData doesn't match the "
                "number of nsIDOMClassInfo ID's, this is bad! Fix it!");
 
--- a/dom/base/nsDOMClassInfoClasses.h
+++ b/dom/base/nsDOMClassInfoClasses.h
@@ -533,8 +533,10 @@ DOMCI_CLASS(DOMError)
 DOMCI_CLASS(DOMRequest)
 DOMCI_CLASS(OpenWindowEventDetail)
 
 DOMCI_CLASS(DOMFileHandle)
 DOMCI_CLASS(FileRequest)
 DOMCI_CLASS(LockedFile)
 
 DOMCI_CLASS(MozActivity)
+
+DOMCI_CLASS(MozTimeManager)
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -217,16 +217,17 @@
 #include "mozAutoDocUpdate.h"
 
 #include "mozilla/Telemetry.h"
 #include "nsLocation.h"
 #include "nsWrapperCacheInlines.h"
 #include "nsDOMEventTargetHelper.h"
 #include "nsIAppsService.h"
 #include "prrng.h"
+#include "TimeChangeObserver.h"
 
 #ifdef ANDROID
 #include <android/log.h>
 #endif
 
 #ifdef PR_LOGGING
 static PRLogModuleInfo* gDOMLeakPRLog;
 #endif
@@ -1050,16 +1051,17 @@ nsGlobalWindow::CleanUp(bool aIgnoreModa
 
   CleanupCachedXBLHandlers(this);
 
   if (mIdleTimer) {
     mIdleTimer->Cancel();
     mIdleTimer = nullptr;
   }
 
+  DisableTimeChangeNotifications();
 #ifdef DEBUG
   nsCycleCollector_DEBUG_shouldBeFreed(static_cast<nsIScriptGlobalObject*>(this));
 #endif
 }
 
 void
 nsGlobalWindow::ClearControllers()
 {
@@ -10615,16 +10617,28 @@ nsGlobalWindow::GetURL(nsIDOMMozURLPrope
     mURLProperty = new nsDOMMozURLProperty(this);
   }
 
   NS_ADDREF(*aURL = mURLProperty);
 
   return NS_OK;
 }
 
+void
+nsGlobalWindow::EnableTimeChangeNotifications()
+{
+  nsSystemTimeChangeObserver::GetInstance()->AddWindowListener(this);
+}
+
+void
+nsGlobalWindow::DisableTimeChangeNotifications()
+{
+  nsSystemTimeChangeObserver::GetInstance()->RemoveWindowListener(this);
+}
+
 // static
 bool
 nsGlobalWindow::HasIndexedDBSupport()
 {
   return Preferences::GetBool("indexedDB.feature.enabled", true);
 }
 
 // static
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -532,16 +532,19 @@ public:
   virtual void SetReadyForFocus();
   virtual void PageHidden();
   virtual nsresult DispatchAsyncHashchange(nsIURI *aOldURI, nsIURI *aNewURI);
   virtual nsresult DispatchSyncPopState();
 
   virtual void EnableDeviceSensor(PRUint32 aType);
   virtual void DisableDeviceSensor(PRUint32 aType);
 
+  virtual void EnableTimeChangeNotifications();
+  virtual void DisableTimeChangeNotifications();
+
   virtual nsresult SetArguments(nsIArray *aArguments, nsIPrincipal *aOrigin);
 
   static bool DOMWindowDumpEnabled();
 
   void MaybeForgiveSpamCount();
   bool IsClosedOrClosing() {
     return (mIsClosed ||
             mInClose ||
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -571,16 +571,19 @@ public:
    */
   virtual void EnableDeviceSensor(PRUint32 aType) = 0;
 
   /**
    * Tell this window that it should remove itself from sensor change notifications.
    */
   virtual void DisableDeviceSensor(PRUint32 aType) = 0;
 
+  virtual void EnableTimeChangeNotifications() = 0;
+  virtual void DisableTimeChangeNotifications() = 0;
+
   /**
    * Set a arguments for this window. This will be set on the window
    * right away (if there's an existing document) and it will also be
    * installed on the window when the next document is loaded. Each
    * language impl is responsible for converting to an array of args
    * as appropriate for that language.
    */
   virtual nsresult SetArguments(nsIArray *aArguments, nsIPrincipal *aOrigin) = 0;
--- a/dom/time/Makefile.in
+++ b/dom/time/Makefile.in
@@ -11,17 +11,25 @@ include $(DEPTH)/config/autoconf.mk
 
 LIBRARY_NAME     = dom_time_s
 XPIDL_MODULE     = dom_time
 LIBXUL_LIBRARY   = 1
 FORCE_STATIC_LIB = 1
 
 include $(topsrcdir)/dom/dom-config.mk
 
-CPPSRCS = $(NULL)
+CPPSRCS = \
+  TimeManager.cpp \
+  TimeChangeObserver.cpp \
+  $(NULL)
+
+EXPORTS = \
+  TimeChangeObserver.h \
+  $(NULL)
 
 XPIDLSRCS = \
   nsIDOMNavigatorTime.idl \
   nsIDOMTimeManager.idl \
   $(NULL)
 
 include $(topsrcdir)/config/config.mk
+include $(topsrcdir)/ipc/chromium/chromium-config.mk
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/dom/time/TimeChangeObserver.cpp
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; 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 "TimeChangeObserver.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/StaticPtr.h"
+#include "nsPIDOMWindow.h"
+#include "nsDOMEvent.h"
+#include "nsContentUtils.h"
+
+using namespace mozilla::hal;
+using namespace mozilla;
+
+StaticAutoPtr<nsSystemTimeChangeObserver> sObserver;
+
+nsSystemTimeChangeObserver* nsSystemTimeChangeObserver::GetInstance()
+{
+  if (!sObserver) {
+    sObserver = new nsSystemTimeChangeObserver();
+    ClearOnShutdown(&sObserver);
+  }
+  return sObserver;
+}
+
+void
+nsSystemTimeChangeObserver::Notify(const SystemTimeChange& aReason)
+{
+  //Copy mWindowListeners and iterate over windowListeners instead because
+  //mWindowListeners may be modified while we loop.
+  nsTArray<nsWeakPtr> windowListeners;
+  for (PRUint32 i = 0; i < mWindowListeners.Length(); i++) {
+    windowListeners.AppendElement(mWindowListeners.SafeElementAt(i));
+  }
+
+  for (PRInt32 i = windowListeners.Length() - 1; i >= 0; i--) {
+    nsCOMPtr<nsIDOMWindow> window = do_QueryReferent(windowListeners[i]);
+    if (!window) {
+      mWindowListeners.RemoveElement(windowListeners[i]);
+      return;
+    }
+
+    nsCOMPtr<nsIDOMDocument> domdoc;
+    window->GetDocument(getter_AddRefs(domdoc));
+    nsCOMPtr<nsIDocument> doc(do_QueryInterface(domdoc));
+    if (!domdoc) {
+      return;
+    }
+
+    nsContentUtils::DispatchTrustedEvent(doc, window,
+      NS_LITERAL_STRING("moztimechange"), /* bubbles = */ true,
+      /* canceable = */ false);
+  }
+}
+
+nsresult
+nsSystemTimeChangeObserver::AddWindowListener(nsIDOMWindow* aWindow)
+{
+  if (!aWindow) {
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+
+  if (mWindowListeners.IndexOf(NS_GetWeakReference(aWindow)) !=
+      nsTArray<nsIDOMWindow*>::NoIndex) {
+    return NS_OK;
+  }
+
+  if (mWindowListeners.Length() == 0) {
+    RegisterSystemTimeChangeObserver(this);
+  }
+
+  mWindowListeners.AppendElement(NS_GetWeakReference(aWindow));
+  return NS_OK;
+}
+
+nsresult
+nsSystemTimeChangeObserver::RemoveWindowListener(nsIDOMWindow *aWindow)
+{
+  mWindowListeners.RemoveElement(NS_GetWeakReference(aWindow));
+
+  if (mWindowListeners.Length() == 0) {
+    UnregisterSystemTimeChangeObserver(this);
+  }
+
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/dom/time/TimeChangeObserver.h
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; 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/. */
+
+#ifndef _mozilla_time_change_observer_h_
+#define _mozilla_time_change_observer_h_
+
+#include "mozilla/Hal.h"
+#include "mozilla/Observer.h"
+#include "mozilla/HalTypes.h"
+#include "nsPIDOMWindow.h"
+#include "nsWeakPtr.h"
+
+typedef mozilla::Observer<mozilla::hal::SystemTimeChange> SystemTimeChangeObserver;
+
+class nsSystemTimeChangeObserver : public SystemTimeChangeObserver
+{
+public:
+  static nsSystemTimeChangeObserver* GetInstance();
+  void Notify(const mozilla::hal::SystemTimeChange& aReason);
+  nsresult AddWindowListener(nsIDOMWindow *aWindow);
+  nsresult RemoveWindowListener(nsIDOMWindow *aWindow);
+private:
+  nsSystemTimeChangeObserver() {};
+  nsTArray<nsWeakPtr> mWindowListeners;
+};
+
+#endif //_mozilla_time_change_observer_h_
new file mode 100644
--- /dev/null
+++ b/dom/time/TimeManager.cpp
@@ -0,0 +1,57 @@
+/* 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 "jsapi.h"
+#include "mozilla/Hal.h"
+#include "nsDOMEvent.h"
+#include "nsDOMEventTargetHelper.h"
+#include "nsIDOMClassInfo.h"
+#include "prtime.h"
+#include "TimeManager.h"
+
+using namespace mozilla::hal;
+
+DOMCI_DATA(MozTimeManager, mozilla::dom::time::TimeManager)
+
+namespace mozilla {
+namespace dom {
+namespace time {
+
+NS_INTERFACE_MAP_BEGIN(TimeManager)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMMozTimeManager)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MozTimeManager)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_ADDREF(TimeManager)
+NS_IMPL_RELEASE(TimeManager)
+
+nsresult
+TimeManager::Set(const JS::Value& date, JSContext* ctx) {
+  double nowMSec = JS_Now() / 1000;
+  double dateMSec;
+
+  if (date.isObject()) {
+    JSObject* dateObj = JSVAL_TO_OBJECT(date);
+
+    if (JS_ObjectIsDate(ctx, dateObj) && js_DateIsValid(ctx, dateObj)) {
+      dateMSec = js_DateGetMsecSinceEpoch(ctx, dateObj);
+    }
+    else {
+      NS_WARN_IF_FALSE(JS_ObjectIsDate(ctx, dateObj), "This is not a Date object");
+      NS_WARN_IF_FALSE(js_DateIsValid(ctx, dateObj), "Date is not valid");
+      return NS_ERROR_INVALID_ARG;
+    }
+  } else if (date.isNumber()) {
+    dateMSec = date.toNumber();
+  } else {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  hal::AdjustSystemClock(JS_DoubleToInt32(dateMSec - nowMSec));
+  return NS_OK;
+}
+
+} // namespace time
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/time/TimeManager.h
@@ -0,0 +1,31 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_time_TimeManager_h
+#define mozilla_dom_time_TimeManager_h
+
+#include "mozilla/HalTypes.h"
+#include "nsIDOMTimeManager.h"
+#include "mozilla/Observer.h"
+
+class nsPIDOMWindow;
+
+namespace mozilla {
+
+typedef Observer<hal::SystemTimeChange> SystemTimeObserver;
+
+namespace dom {
+namespace time {
+class TimeManager : public nsIDOMMozTimeManager
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMMOZTIMEMANAGER
+};
+
+} // namespace time
+} // namespace dom
+} // namespace mozilla
+
+#endif //mozilla_dom_time_TimeManager_h
--- a/dom/time/nsIDOMTimeManager.idl
+++ b/dom/time/nsIDOMTimeManager.idl
@@ -2,11 +2,19 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
 [scriptable, builtinclass, uuid(d29beaaa-bd54-4fd5-9f18-e0eedb1dc96d)]
 interface nsIDOMMozTimeManager : nsISupports
 {
-  // jsval could be Date object or UTC seconds
+  /* Set the system time.
+   *
+   * The |time| argument can be either a Date object or a number.
+   *
+   * - If |time| is a number, it's interpreted as seconds since the epoch 
+   *   (midnight UTC on January 1, 1970)
+   * - If |time| is a Date object, |set(time)| is equivalent to 
+   *   |set(time.getTime())|.
+   */
   [implicit_jscontext] void set(in jsval time);
 };
--- a/layout/build/Makefile.in
+++ b/layout/build/Makefile.in
@@ -78,16 +78,17 @@ SHARED_LIBRARY_LIBS = \
 	$(DEPTH)/dom/src/offline/$(LIB_PREFIX)jsdomoffline_s.$(LIB_SUFFIX) \
 	$(DEPTH)/dom/src/geolocation/$(LIB_PREFIX)jsdomgeolocation_s.$(LIB_SUFFIX) \
 	$(DEPTH)/dom/src/notification/$(LIB_PREFIX)jsdomnotification_s.$(LIB_SUFFIX) \
 	$(DEPTH)/dom/system/$(LIB_PREFIX)domsystem_s.$(LIB_SUFFIX) \
 	$(DEPTH)/dom/workers/$(LIB_PREFIX)domworkers_s.$(LIB_SUFFIX) \
 	$(DEPTH)/dom/indexedDB/$(LIB_PREFIX)dom_indexeddb_s.$(LIB_SUFFIX) \
 	$(DEPTH)/dom/indexedDB/ipc/$(LIB_PREFIX)dom_indexeddb_ipc_s.$(LIB_SUFFIX) \
 	$(DEPTH)/dom/browser-element/$(LIB_PREFIX)dom_browserelement_s.$(LIB_SUFFIX) \
+	$(DEPTH)/dom/time/$(LIB_PREFIX)dom_time_s.$(LIB_SUFFIX) \
 	$(DEPTH)/editor/libeditor/text/$(LIB_PREFIX)texteditor_s.$(LIB_SUFFIX) \
 	$(DEPTH)/editor/libeditor/base/$(LIB_PREFIX)editorbase_s.$(LIB_SUFFIX) \
 	$(DEPTH)/parser/html/$(LIB_PREFIX)html5p_s.$(LIB_SUFFIX) \
 	$(DEPTH)/caps/src/$(LIB_PREFIX)caps_s.$(LIB_SUFFIX) \
 	$(DEPTH)/editor/libeditor/html/$(LIB_PREFIX)htmleditor_s.$(LIB_SUFFIX) \
 	$(DEPTH)/editor/txtsvc/src/$(LIB_PREFIX)txtsvc_s.$(LIB_SUFFIX) \
 	$(DEPTH)/content/mathml/content/src/$(LIB_PREFIX)gkcontentmathml_s.$(LIB_SUFFIX) \
 	$(NULL)
--- a/widget/nsGUIEvent.h
+++ b/widget/nsGUIEvent.h
@@ -532,16 +532,19 @@ class nsHashKey;
 #define NS_TOUCH_LEAVE               (NS_TOUCH_EVENT_START+4)
 #define NS_TOUCH_CANCEL              (NS_TOUCH_EVENT_START+5)
 
 // Pointerlock DOM API
 #define NS_POINTERLOCK_START         5300
 #define NS_POINTERLOCKCHANGE         (NS_POINTERLOCK_START)
 #define NS_POINTERLOCKERROR          (NS_POINTERLOCK_START + 1)
 
+//System time is changed
+#define NS_MOZ_TIME_CHANGE_EVENT     5400
+
 /**
  * Return status for event processors, nsEventStatus, is defined in
  * nsEvent.h.
  */
 
 /**
  * different types of (top-level) window z-level positioning
  */