Bug 1432429 JS fuzzy time update 8/14 draft
authorAndrea Marchesini <amarchesini@mozilla.com>
Fri, 15 Jun 2018 19:10:51 -0700
changeset 826369 16705fe987bc10e810b9b01bc2838f58f14e09ce
parent 826368 f1ae2971ce0a0d7a180fb8560cc4d6a103a94b62
child 826370 018de98134d427b06784ce883045e4c95fdf028e
push id118310
push userbmo:tom@mozilla.com
push dateFri, 03 Aug 2018 18:22:17 +0000
bugs1432429
milestone62.0a1
Bug 1432429 JS fuzzy time update 8/14 MozReview-Commit-ID: EH8XsX6Ubjf
js/src/jsapi.cpp
js/src/jsapi.h
js/src/vm/FuzzyfoxClock.h
js/src/vm/Runtime.cpp
js/src/vm/Runtime.h
js/src/vm/Time.cpp
js/xpconnect/src/FuzzyfoxClockUpdater.cpp
js/xpconnect/src/FuzzyfoxClockUpdater.h
js/xpconnect/src/XPCJSRuntime.cpp
js/xpconnect/src/XPCRuntimeService.cpp
js/xpconnect/src/moz.build
js/xpconnect/src/xpcprivate.h
netwerk/protocol/http/nsHttpChannel.cpp
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -5701,16 +5701,24 @@ JS_RequestInterruptCallback(JSContext* c
 }
 
 JS_PUBLIC_API(void)
 JS_RequestInterruptCallbackCanWait(JSContext* cx)
 {
     cx->requestInterrupt(InterruptReason::CallbackCanWait);
 }
 
+#ifdef MOZ_FUZZYFOX
+JS_PUBLIC_API(void)
+JS_UpdateLockedClock(JSContext* cx, int64_t update)
+{
+    cx->runtime()->updateLockedClock(update);
+}
+#endif
+
 JS::AutoSetAsyncStackForNewCalls::AutoSetAsyncStackForNewCalls(
   JSContext* cx, HandleObject stack, const char* asyncCause,
   JS::AutoSetAsyncStackForNewCalls::AsyncCallKind kind)
   : cx(cx),
     oldAsyncStack(cx, cx->asyncStackForNewActivations()),
     oldAsyncCause(cx->asyncCauseForNewActivations),
     oldAsyncCallIsExplicit(cx->asyncCallIsExplicit)
 {
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -5483,16 +5483,21 @@ extern JS_PUBLIC_API(bool)
 JS_GetPendingException(JSContext* cx, JS::MutableHandleValue vp);
 
 extern JS_PUBLIC_API(void)
 JS_SetPendingException(JSContext* cx, JS::HandleValue v);
 
 extern JS_PUBLIC_API(void)
 JS_ClearPendingException(JSContext* cx);
 
+#ifdef MOZ_FUZZYFOX
+extern JS_PUBLIC_API(void)
+JS_UpdateLockedClock(JSContext* cx, int64_t update);
+#endif
+
 namespace JS {
 
 /**
  * Save and later restore the current exception state of a given JSContext.
  * This is useful for implementing behavior in C++ that's like try/catch
  * or try/finally in JS.
  *
  * Typical usage:
new file mode 100644
--- /dev/null
+++ b/js/src/vm/FuzzyfoxClock.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 FuzzyfoxClock_h
+#define FuzzyfoxClock_h
+
+namespace js {
+
+/* Locked clock impl*/
+class FuzzyfoxClock
+{
+  public:
+    FuzzyfoxClock() : currentClock(0)
+    {}
+
+  int64_t
+  getFuzzyfoxClock() const
+  {
+    return currentClock;
+  }
+
+  bool
+  isClockStarted() const
+  {
+    return currentClock;
+  }
+
+  void
+  updateFuzzyfoxClock(int64_t update)
+  {
+    currentClock = update;
+  }
+
+  private:
+    int64_t currentClock;
+};
+
+} // js namespace
+
+#endif /* FuzzyfoxClock_h*/
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -806,16 +806,36 @@ void
 JSRuntime::setUsedByHelperThread(Zone* zone)
 {
     MOZ_ASSERT(!zone->usedByHelperThread());
     MOZ_ASSERT(!zone->wasGCStarted());
     zone->setUsedByHelperThread();
     numActiveHelperThreadZones++;
 }
 
+#ifdef MOZ_FUZZYFOX
+void
+JSRuntime::updateLockedClock(int64_t update)
+{
+    fuzzyfoxClock.updateFuzzyfoxClock(update);
+}
+
+bool
+JSRuntime::hasValidFuzzyfoxClock() const
+{
+    return fuzzyfoxClock.isClockStarted();
+}
+
+int64_t
+JSRuntime::getFuzzyfoxClock() const
+{
+    return fuzzyfoxClock.getFuzzyfoxClock();
+}
+#endif
+
 void
 JSRuntime::clearUsedByHelperThread(Zone* zone)
 {
     MOZ_ASSERT(zone->usedByHelperThread());
     zone->clearUsedByHelperThread();
     numActiveHelperThreadZones--;
     JSContext* cx = mainContextFromOwnThread();
     if (gc.fullGCForAtomsRequested() && cx->canCollectAtoms())
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -37,16 +37,19 @@
 #endif
 #include "js/UniquePtr.h"
 #include "js/Vector.h"
 #include "threading/Thread.h"
 #include "vm/Caches.h"
 #include "vm/CodeCoverage.h"
 #include "vm/CommonPropertyNames.h"
 #include "vm/DateTime.h"
+#ifdef MOZ_FUZZYFOX
+#include "vm/FuzzyfoxClock.h"
+#endif
 #include "vm/GeckoProfiler.h"
 #include "vm/JSAtom.h"
 #include "vm/JSScript.h"
 #include "vm/Scope.h"
 #include "vm/SharedImmutableStringsCache.h"
 #include "vm/Stack.h"
 #include "vm/Stopwatch.h"
 #include "vm/SymbolType.h"
@@ -961,16 +964,26 @@ struct JSRuntime : public js::MallocProv
         bool isExecuting;
 
         // if non-null, any call to `setPendingException`
         // in this runtime will trigger the call to `interceptor`
         JSErrorInterceptor* interceptor;
     };
     ErrorInterceptionSupport errorInterception;
 #endif // defined(NIGHTLY_BUILD)
+
+#ifdef MOZ_FUZZYFOX
+  public:
+    void updateLockedClock(int64_t update);
+    bool hasValidFuzzyfoxClock() const;
+    int64_t getFuzzyfoxClock() const;
+
+  private:
+    js::FuzzyfoxClock fuzzyfoxClock;
+#endif
 };
 
 namespace js {
 
 /*
  * RAII class that takes the GC lock while it is live.
  *
  * Usually functions will pass const references of this class.  However
--- a/js/src/vm/Time.cpp
+++ b/js/src/vm/Time.cpp
@@ -6,16 +6,18 @@
 
 /* PR time code. */
 
 #include "vm/Time.h"
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/MathAlgorithms.h"
 
+#include "vm/JSContext.h"
+
 #ifdef SOLARIS
 #define _REENTRANT 1
 #endif
 #include <string.h>
 #include <time.h>
 
 #include "jstypes.h"
 #include "jsutil.h"
@@ -42,16 +44,22 @@ extern int gettimeofday(struct timeval* 
 #endif /* XP_UNIX */
 
 using mozilla::DebugOnly;
 
 #if defined(XP_UNIX)
 int64_t
 PRMJ_Now()
 {
+#ifdef MOZ_FUZZYFOX
+    JSContext* cx = js::TlsContext.get();
+    if (cx && cx->runtime()->hasValidFuzzyfoxClock())
+        return cx->runtime()->getFuzzyfoxClock();
+#endif
+
     struct timeval tv;
 
 #ifdef _SVID_GETTOD   /* Defined only on Solaris, see Solaris <sys/types.h> */
     gettimeofday(&tv);
 #else
     gettimeofday(&tv, 0);
 #endif /* _SVID_GETTOD */
 
@@ -148,16 +156,22 @@ PRMJ_NowShutdown()
 #define MUTEX_LOCK(m) EnterCriticalSection(m)
 #define MUTEX_UNLOCK(m) LeaveCriticalSection(m)
 #define MUTEX_SETSPINCOUNT(m, c) SetCriticalSectionSpinCount((m),(c))
 
 // Please see bug 363258 for why the win32 timing code is so complex.
 int64_t
 PRMJ_Now()
 {
+#ifdef MOZ_FUZZYFOX
+    JSContext* cx = js::TlsContext.get();
+    if (cx && cx->runtime()->hasValidFuzzyfoxClock())
+        return cx->runtime()->getFuzzyfoxClock();
+#endif
+
     if (pGetSystemTimePreciseAsFileTime) {
         // Windows 8 has a new API function that does all the work.
         FILETIME ft;
         pGetSystemTimePreciseAsFileTime(&ft);
         return int64_t(FileTimeToUnixMicroseconds(ft));
     }
 
     bool calibrated = false;
new file mode 100644
--- /dev/null
+++ b/js/xpconnect/src/FuzzyfoxClockUpdater.cpp
@@ -0,0 +1,120 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "FuzzyfoxClockUpdater.h"
+#include "mozilla/CycleCollectedJSContext.h"
+#include "nsThreadUtils.h"
+#include "nsCOMPtr.h"
+
+using namespace mozilla;
+
+/* static */ already_AddRefed<FuzzyfoxClockUpdater>
+FuzzyfoxClockUpdater::Create()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  RefPtr<FuzzyfoxClockUpdater> holder = new FuzzyfoxClockUpdater();
+  if (!holder->Initialize()) {
+    return nullptr;
+  }
+
+  return holder.forget();
+}
+
+FuzzyfoxClockUpdater::FuzzyfoxClockUpdater()
+  : mShutdown(false)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+}
+
+FuzzyfoxClockUpdater::~FuzzyfoxClockUpdater()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+}
+
+bool
+FuzzyfoxClockUpdater::Initialize()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+  if (NS_WARN_IF(!obs)) {
+    return false;
+  }
+
+  nsresult rv = obs->AddObserver(this, "fuzzyfox-update-clocks", false);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+
+  rv = obs->AddObserver(this, "xpcom-shutdown", false);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+
+  return true;
+}
+
+NS_IMETHODIMP
+FuzzyfoxClockUpdater::Observe(nsISupports* aSubject, const char* aTopic,
+                             const char16_t* aData)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!strcmp(aTopic, "fuzzyfox-update-clocks")) {
+    nsCOMPtr<nsISupportsPRInt64> wrapper = do_QueryInterface(aSubject);
+    if (NS_WARN_IF(!wrapper)) {
+      return NS_OK;
+    }
+
+    int64_t newTime = 0;
+    nsresult rv = wrapper->GetData(&newTime);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return NS_OK;
+    }
+
+    CycleCollectedJSContext* ccjscx = CycleCollectedJSContext::Get();
+    if (!ccjscx) {
+      return NS_OK;
+    }
+
+    JSContext* cx = ccjscx->Context();
+    MOZ_ASSERT(cx);
+
+    JS_UpdateLockedClock(cx, newTime);
+    return NS_OK;
+  }
+
+  MOZ_ASSERT(!strcmp(aTopic, "xpcom-shutdown"));
+  Shutdown();
+  return NS_OK;
+}
+
+void
+FuzzyfoxClockUpdater::Shutdown()
+{
+  if (mShutdown) {
+    return;
+  }
+
+  mShutdown = true;
+
+  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+  if (NS_WARN_IF(!obs)) {
+    return;
+  }
+
+  obs->RemoveObserver(this, "fuzzyfox-update-clocks");
+  obs->RemoveObserver(this, "xpcom-shutdown");
+}
+
+NS_INTERFACE_MAP_BEGIN(FuzzyfoxClockUpdater)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
+  NS_INTERFACE_MAP_ENTRY(nsIObserver)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_ADDREF(FuzzyfoxClockUpdater)
+NS_IMPL_RELEASE(FuzzyfoxClockUpdater)
new file mode 100644
--- /dev/null
+++ b/js/xpconnect/src/FuzzyfoxClockUpdater.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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_FuzzyfoxClockUpdater_h
+#define mozilla_FuzzyfoxClockUpdater_h
+
+#include "nsIObserver.h"
+
+namespace mozilla {
+
+// This class holds a FuzzyFoxClock object and it keeps updated.
+class FuzzyfoxClockUpdater final : public nsIObserver
+{
+ public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+
+  static already_AddRefed<FuzzyfoxClockUpdater>
+  Create();
+
+  void
+  Shutdown();
+
+private:
+  FuzzyfoxClockUpdater();
+  ~FuzzyfoxClockUpdater();
+
+  bool
+  Initialize();
+
+  bool
+  mShutdown;
+};
+
+} // mozilla namespace
+
+#endif /* mozilla_FuzzyfoxClockUpdater_h */
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -51,16 +51,20 @@
 #include "mozilla/ProcessHangMonitor.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/UniquePtrExtensions.h"
 #include "mozilla/Unused.h"
 #include "AccessCheck.h"
 #include "nsGlobalWindow.h"
 #include "nsAboutProtocolUtils.h"
 
+#ifdef MOZ_FUZZYFOX
+#include "FuzzyfoxClockUpdater.h"
+#endif
+
 #include "GeckoProfiler.h"
 #include "nsIInputStream.h"
 #include "nsIXULRuntime.h"
 #include "nsJSPrincipals.h"
 
 #ifdef XP_WIN
 #include <windows.h>
 #endif
@@ -1063,16 +1067,23 @@ XPCJSRuntime::Shutdown(JSContext* cx)
     mClassInfo2NativeSetMap = nullptr;
 
     delete mNativeSetMap;
     mNativeSetMap = nullptr;
 
     delete mDyingWrappedNativeProtoMap;
     mDyingWrappedNativeProtoMap = nullptr;
 
+#ifdef MOZ_FUZZYFOX
+    if (mFuzzyfoxClockUpdater) {
+        mFuzzyfoxClockUpdater->Shutdown();
+        mFuzzyfoxClockUpdater = nullptr;
+    }
+#endif
+
     CycleCollectedJSRuntime::Shutdown(cx);
 }
 
 XPCJSRuntime::~XPCJSRuntime()
 {
     MOZ_COUNT_DTOR_INHERITED(XPCJSRuntime, CycleCollectedJSRuntime);
 }
 
@@ -2878,16 +2889,20 @@ XPCJSRuntime::Initialize(JSContext* cx)
     RegisterStrongMemoryReporter(new JSMainRuntimeTemporaryPeakReporter());
     RegisterJSMainRuntimeGCHeapDistinguishedAmount(JSMainRuntimeGCHeapDistinguishedAmount);
     RegisterJSMainRuntimeTemporaryPeakDistinguishedAmount(JSMainRuntimeTemporaryPeakDistinguishedAmount);
     RegisterJSMainRuntimeRealmsSystemDistinguishedAmount(JSMainRuntimeRealmsSystemDistinguishedAmount);
     RegisterJSMainRuntimeRealmsUserDistinguishedAmount(JSMainRuntimeRealmsUserDistinguishedAmount);
     mozilla::RegisterJSSizeOfTab(JSSizeOfTab);
 
     xpc_LocalizeRuntime(JS_GetRuntime(cx));
+
+#ifdef MOZ_FUZZYFOX
+    mFuzzyfoxClockUpdater = FuzzyfoxClockUpdater::Create();
+#endif
 }
 
 bool
 XPCJSRuntime::InitializeStrings(JSContext* cx)
 {
     JSAutoRequest ar(cx);
 
     // if it is our first context then we need to generate our string ids
--- a/js/xpconnect/src/XPCRuntimeService.cpp
+++ b/js/xpconnect/src/XPCRuntimeService.cpp
@@ -25,16 +25,17 @@ NS_IMPL_ISUPPORTS(BackstagePass,
                        XPC_SCRIPTABLE_WANT_ENUMERATE | \
                        XPC_SCRIPTABLE_WANT_FINALIZE | \
                        XPC_SCRIPTABLE_WANT_PRECREATE | \
                        XPC_SCRIPTABLE_USE_JSSTUB_FOR_ADDPROPERTY |  \
                        XPC_SCRIPTABLE_USE_JSSTUB_FOR_DELPROPERTY |  \
                        XPC_SCRIPTABLE_DONT_ENUM_QUERY_INTERFACE |  \
                        XPC_SCRIPTABLE_IS_GLOBAL_OBJECT |  \
                        XPC_SCRIPTABLE_DONT_REFLECT_INTERFACE_NAMES)
+#include "xpc_make_class.h"
 #include "xpc_map_end.h" /* This will #undef the above */
 
 
 JSObject*
 BackstagePass::GetGlobalJSObject()
 {
     if (mWrapper)
         return mWrapper->GetFlatJSObject();
--- a/js/xpconnect/src/moz.build
+++ b/js/xpconnect/src/moz.build
@@ -38,16 +38,21 @@ UNIFIED_SOURCES += [
     'XPCWrappedNative.cpp',
     'XPCWrappedNativeInfo.cpp',
     'XPCWrappedNativeJSOps.cpp',
     'XPCWrappedNativeProto.cpp',
     'XPCWrappedNativeScope.cpp',
     'XPCWrapper.cpp',
 ]
 
+if CONFIG['MOZ_FUZZYFOX']:
+    UNIFIED_SOURCES += [
+        'FuzzyfoxClockUpdater.cpp',
+    ]
+
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
     '../loader',
     '../wrappers',
     '/caps',
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -155,19 +155,25 @@
 #undef GetClassInfo
 #endif
 #ifdef GetClassName
 #undef GetClassName
 #endif
 #endif /* XP_WIN */
 
 namespace mozilla {
+
+#ifdef MOZ_FUZZYFOX
+class FuzzyfoxClockUpdater;
+#endif
+
 namespace dom {
 class Exception;
 } // namespace dom
+
 } // namespace mozilla
 
 /***************************************************************************/
 // default initial sizes for maps (hashtables)
 
 #define XPC_JS_MAP_LENGTH                       32
 #define XPC_JS_CLASS_MAP_LENGTH                 32
 
@@ -612,16 +618,20 @@ private:
     nsTArray<xpcGCCallback> extraGCCallbacks;
     JS::GCSliceCallback mPrevGCSliceCallback;
     JS::DoCycleCollectionCallback mPrevDoCycleCollectionCallback;
     JS::PersistentRootedObject mUnprivilegedJunkScope;
     JS::PersistentRootedObject mPrivilegedJunkScope;
     JS::PersistentRootedObject mCompilationScope;
     RefPtr<AsyncFreeSnowWhite> mAsyncSnowWhiteFreer;
 
+#ifdef MOZ_FUZZYFOX
+    RefPtr<mozilla::FuzzyfoxClockUpdater> mFuzzyfoxClockUpdater;
+#endif
+
     friend class XPCJSContext;
     friend class XPCIncrementalReleaseRunnable;
 };
 
 inline JS::HandleId
 XPCJSContext::GetStringID(unsigned index) const
 {
     return Runtime()->GetStringID(index);
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -6026,26 +6026,29 @@ nsHttpChannel::AsyncOpen(nsIStreamListen
     return NS_OK;
 }
 
 nsresult
 nsHttpChannel::AsyncOpenFinal(TimeStamp aTimeStamp)
 {
     // Added due to PauseTask/DelayHttpChannel
     nsresult rv;
+    TimeStamp now = aTimeStamp;
+#else
+    TimeStamp now = TimeStamp::Now();
 #endif
 
     if (mLoadGroup)
         mLoadGroup->AddRequest(this, nullptr);
 
     // record asyncopen time unconditionally and clear it if we
     // don't want it after OnModifyRequest() weighs in. But waiting for
     // that to complete would mean we don't include proxy resolution in the
     // timing.
-    mAsyncOpenTime = aTimeStamp;
+    mAsyncOpenTime = now;
 
     // Remember we have Authorization header set here.  We need to check on it
     // just once and early, AsyncOpen is the best place.
     mCustomAuthHeader = mRequestHead.HasHeader(nsHttp::Authorization);
 
     // The common case for HTTP channels is to begin proxy resolution and return
     // at this point. The only time we know mProxyInfo already is if we're
     // proxying a non-http protocol like ftp. We don't need to discover proxy