Bug 77992 part 2 - Make event.timeStamp use TimeStamp value based on a pref; r=smaug
authorBrian Birtles <birtles@gmail.com>
Fri, 06 Jun 2014 14:29:49 +0900
changeset 206233 2820efae73160c047ac2323fcb32db2e00af8b29
parent 206232 16f49acfc4e9cb839c5027ede91bfac9d93c795c
child 206234 4fa7d44e5eb3c8c7d402df9a5944d539eeb2445f
push id3741
push userasasaki@mozilla.com
push dateMon, 21 Jul 2014 20:25:18 +0000
treeherdermozilla-beta@4d6f46f5af68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs77992
milestone32.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
Bug 77992 part 2 - Make event.timeStamp use TimeStamp value based on a pref; r=smaug Note that this pref isn't fully live since the timeStamp member of the event interface is [Pure] and so values will be cached.
dom/events/Event.cpp
dom/events/Event.h
dom/smil/test/test_smilTimeEvents.xhtml
editor/libeditor/html/tests/test_dom_input_event_on_htmleditor.html
editor/libeditor/text/tests/test_dom_input_event_on_texteditor.html
modules/libpref/src/init/all.js
--- a/dom/events/Event.cpp
+++ b/dom/events/Event.cpp
@@ -23,27 +23,32 @@
 #include "nsGlobalWindow.h"
 #include "nsIFrame.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "nsIPresShell.h"
 #include "nsIScrollableFrame.h"
 #include "nsJSEnvironment.h"
 #include "nsLayoutUtils.h"
+#include "nsPerformance.h"
 #include "nsPIWindowRoot.h"
+#include "WorkerPrivate.h"
 
 namespace mozilla {
 namespace dom {
 
 namespace workers {
 extern bool IsCurrentThreadRunningChromeWorker();
 } // namespace workers
 
 static char *sPopupAllowedEvents;
 
+static bool sReturnHighResTimeStamp = false;
+static bool sReturnHighResTimeStampIsSet = false;
+
 Event::Event(EventTarget* aOwner,
              nsPresContext* aPresContext,
              WidgetEvent* aEvent)
 {
   ConstructorInit(aOwner, aPresContext, aEvent);
 }
 
 Event::Event(nsPIDOMWindow* aParent)
@@ -58,16 +63,23 @@ Event::ConstructorInit(EventTarget* aOwn
 {
   SetIsDOMBinding();
   SetOwner(aOwner);
   mIsMainThreadEvent = mOwner || NS_IsMainThread();
   if (mIsMainThreadEvent) {
     nsJSContext::LikelyShortLivingObjectCreated();
   }
 
+  if (mIsMainThreadEvent && !sReturnHighResTimeStampIsSet) {
+    Preferences::AddBoolVarCache(&sReturnHighResTimeStamp,
+                                 "dom.event.highrestimestamp.enabled",
+                                 sReturnHighResTimeStamp);
+    sReturnHighResTimeStampIsSet = true;
+  }
+
   mPrivateDataDuplicated = false;
 
   if (aEvent) {
     mEvent = aEvent;
     mEventIsInternal = false;
   }
   else {
     mEventIsInternal = true;
@@ -953,16 +965,56 @@ Event::DefaultPrevented(JSContext* aCx) 
   }
 
   // If preventDefault() has been called by content, return true.  Otherwise,
   // i.e., preventDefault() has been called by chrome, return true only when
   // this is called by chrome.
   return mEvent->mFlags.mDefaultPreventedByContent || IsChrome(aCx);
 }
 
+uint64_t
+Event::TimeStamp() const
+{
+  if (!sReturnHighResTimeStamp) {
+    return mEvent->time;
+  }
+
+  if (mEvent->timeStamp.IsNull()) {
+    return 0L;
+  }
+
+  if (mIsMainThreadEvent) {
+    if (NS_WARN_IF(!mOwner)) {
+      return 0L;
+    }
+
+    nsPerformance* perf = mOwner->GetPerformance();
+    if (NS_WARN_IF(!perf)) {
+      return 0L;
+    }
+
+    DOMHighResTimeStamp eventTime =
+      perf->GetDOMTiming()->TimeStampToDOMHighRes(mEvent->timeStamp);
+    return static_cast<uint64_t>(eventTime);
+  }
+
+  // For dedicated workers, we should make times relative to the navigation
+  // start of the document that created the worker. We currently don't have
+  // that information handy so for now we treat shared workers and dedicated
+  // workers alike and make times relative to the worker creation time. We can
+  // fix this when we implement WorkerPerformance.
+  workers::WorkerPrivate* workerPrivate =
+    workers::GetCurrentThreadWorkerPrivate();
+  MOZ_ASSERT(workerPrivate);
+
+  TimeDuration duration =
+    mEvent->timeStamp - workerPrivate->CreationTimeStamp();
+  return static_cast<uint64_t>(duration.ToMilliseconds());
+}
+
 bool
 Event::GetPreventDefault() const
 {
   if (mOwner) {
     if (nsIDocument* doc = mOwner->GetExtantDoc()) {
       doc->WarnOnceAbout(nsIDocument::eGetPreventDefault);
     }
   }
--- a/dom/events/Event.h
+++ b/dom/events/Event.h
@@ -188,20 +188,17 @@ public:
     return mEvent->mFlags.mIsTrusted;
   }
 
   bool IsSynthesized() const
   {
     return mEvent->mFlags.mIsSynthesizedForTests;
   }
 
-  uint64_t TimeStamp() const
-  {
-    return mEvent->time;
-  }
+  uint64_t TimeStamp() const;
 
   void InitEvent(const nsAString& aType, bool aBubbles, bool aCancelable,
                  ErrorResult& aRv)
   {
     aRv = InitEvent(aType, aBubbles, aCancelable);
   }
 
   EventTarget* GetOriginalTarget() const;
--- a/dom/smil/test/test_smilTimeEvents.xhtml
+++ b/dom/smil/test/test_smilTimeEvents.xhtml
@@ -230,22 +230,24 @@ function handleOnEnd(evt)
 
 function sanityCheckEvent(evt)
 {
   is(evt.target, gAnim, "Unexpected event target");
   is(evt.currentTarget, gAnim, "Unexpected event current target");
   is(evt.eventPhase, evt.AT_TARGET);
   is(evt.bubbles, false, "Event should not bubble");
   is(evt.cancelable, false, "Event should not be cancelable");
-  // Currently we set event timestamps to 0 which DOM 2 allows. This isn't
-  // correct since SMIL uses this field to avoid synchronisation slew but first
-  // we need to fix bug 323039 and bug 77992 which involve storing timestamps as
-  // 64-bit integers and deciding whether those timestamps should be related to
-  // the epoch or system start.
-  is(evt.timeStamp, 0, "Event timeStamp should be 0");
+  if (SpecialPowers.getBoolPref("dom.event.highrestimestamp.enabled")) {
+    var now = window.performance.now();
+    ok(evt.timeStamp > 0 && evt.timeStamp < now,
+       "Event timeStamp (" + evt.timeStamp + ") should be > 0 but " +
+       "before the current time (" + now + ")");
+  } else {
+    is(evt.timeStamp, 0, "Event timeStamp should be 0");
+  }
   ok(evt.view !== null, "Event view not set");
 }
 
 function checkExpectedEvent(evt)
 {
   sanityCheckEvent(evt);
   ok(gExpectedEvents.length > 0, "Unexpected event: " + evt.type);
   if (gExpectedEvents.length == 0) return;
--- a/editor/libeditor/html/tests/test_dom_input_event_on_htmleditor.html
+++ b/editor/libeditor/html/tests/test_dom_input_event_on_htmleditor.html
@@ -62,20 +62,31 @@ function runTests()
 
     var inputEvent = null;
 
     var handler = function (aEvent) {
       is(aEvent.target, eventTarget,
          "input event is fired on unexpected element: " + aEvent.target.tagName);
       ok(!aEvent.cancelable, "input event must not be cancelable");
       ok(aEvent.bubbles, "input event must be bubbles");
-      var eventTime = new Date(aEvent.timeStamp);
-      var duration = Math.abs(Date.now() - aEvent.timeStamp);
-      ok(duration < 30 * 1000,
-         "perhaps, timestamp wasn't set correctly :" + eventTime.toLocaleString());
+      if (SpecialPowers.getBoolPref("dom.event.highrestimestamp.enabled")) {
+        var duration = Math.abs(window.performance.now() - aEvent.timeStamp);
+        ok(duration < 30 * 1000,
+           "perhaps, timestamp wasn't set correctly :" + aEvent.timeStamp +
+           " (expected it to be within 30s of the current time but it " +
+           "differed by " + duration + "ms)");
+      } else {
+        var eventTime = new Date(aEvent.timeStamp);
+        var duration = Math.abs(Date.now() - aEvent.timeStamp);
+        ok(duration < 30 * 1000,
+           "perhaps, timestamp wasn't set correctly :" +
+           eventTime.toLocaleString() +
+           " (expected it to be within 30s of the current time but it " +
+           "differed by " + duration + "ms)");
+      }
       inputEvent = aEvent;
     };
 
     aWindow.addEventListener("input", handler, true);
 
     inputEvent = null;
     synthesizeKey("a", { }, aWindow);
     is(editTarget.innerHTML, "a", aDescription + "wrong element was edited");
--- a/editor/libeditor/text/tests/test_dom_input_event_on_texteditor.html
+++ b/editor/libeditor/text/tests/test_dom_input_event_on_texteditor.html
@@ -36,20 +36,31 @@ function runTests()
 
     var inputEvent = null;
 
     var handler = function (aEvent) {
       is(aEvent.target, aElement,
          "input event is fired on unexpected element: " + aEvent.target.tagName);
       ok(!aEvent.cancelable, "input event must not be cancelable");
       ok(aEvent.bubbles, "input event must be bubbles");
-      var eventTime = new Date(aEvent.timeStamp);
-      var duration = Math.abs(Date.now() - aEvent.timeStamp);
-      ok(duration < 30 * 1000,
-         "perhaps, timestamp wasn't set correctly :" + eventTime.toLocaleString());
+      if (SpecialPowers.getBoolPref("dom.event.highrestimestamp.enabled")) {
+        var duration = Math.abs(window.performance.now() - aEvent.timeStamp);
+        ok(duration < 30 * 1000,
+           "perhaps, timestamp wasn't set correctly :" + aEvent.timeStamp +
+           " (expected it to be within 30s of the current time but it " +
+           "differed by " + duration + "ms)");
+      } else {
+        var eventTime = new Date(aEvent.timeStamp);
+        var duration = Math.abs(Date.now() - aEvent.timeStamp);
+        ok(duration < 30 * 1000,
+           "perhaps, timestamp wasn't set correctly :" +
+           eventTime.toLocaleString() +
+           " (expected it to be within 30s of the current time but it " +
+           "differed by " + duration + "ms)");
+      }
       inputEvent = aEvent;
     };
 
     aElement.addEventListener("input", handler, true);
 
     inputEvent = null;
     synthesizeKey("a", { });
     is(aElement.value, "a", aDescription + "'a' key didn't change the value");
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -793,16 +793,21 @@ pref("privacy.popups.disable_from_plugin
 // "do not track" HTTP header, disabled by default
 pref("privacy.donottrackheader.enabled",    false);
 //   0 = tracking is acceptable
 //   1 = tracking is unacceptable
 pref("privacy.donottrackheader.value",      1);
 
 pref("dom.event.contextmenu.enabled",       true);
 pref("dom.event.clipboardevents.enabled",   true);
+#if defined(XP_WIN) && !defined(RELEASE_BUILD)
+pref("dom.event.highrestimestamp.enabled",  true);
+#else
+pref("dom.event.highrestimestamp.enabled",  false);
+#endif
 
 pref("dom.webcomponents.enabled",           false);
 
 pref("javascript.enabled",                  true);
 pref("javascript.options.strict",           false);
 #ifdef DEBUG
 pref("javascript.options.strict.debug",     true);
 #endif