Bug 908390 part 2. Implement performance.now() on workers. r=khuey
authorBoris Zbarsky <bzbarsky@mit.edu>
Fri, 29 Aug 2014 19:50:06 -0400
changeset 224171 02d549361c6d81bd108867ad544f3e5f341187ab
parent 224170 f79c41150d2b960a708be3d12bb9fa1b82e2e72e
child 224172 fe0200fd2d3ac7a8d325af8969f8f52cb07bbae6
push id3979
push userraliiev@mozilla.com
push dateMon, 13 Oct 2014 16:35:44 +0000
treeherdermozilla-beta@30f2cc610691 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskhuey
bugs908390
milestone34.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 908390 part 2. Implement performance.now() on workers. r=khuey
dom/bindings/Bindings.conf
dom/webidl/Performance.webidl
dom/webidl/WorkerGlobalScope.webidl
dom/workers/Performance.cpp
dom/workers/Performance.h
dom/workers/WorkerPrivate.cpp
dom/workers/WorkerPrivate.h
dom/workers/WorkerScope.cpp
dom/workers/WorkerScope.h
dom/workers/moz.build
dom/workers/test/mochitest.ini
dom/workers/test/test_worker_interfaces.js
dom/workers/test/test_worker_performance_now.html
dom/workers/test/test_worker_performance_now.js
dom/workers/test/worker_driver.js
dom/workers/test/worker_wrapper.js
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -954,20 +954,25 @@ DOMInterfaces = {
 },
 
 'PeerConnectionImpl': {
     'nativeType': 'sipcc::PeerConnectionImpl',
     'headerFile': 'PeerConnectionImpl.h',
     'wrapperCache': False
 },
 
-'Performance': {
+'Performance': [{
     'nativeType': 'nsPerformance',
     'resultNotAddRefed': [ 'timing', 'navigation' ]
 },
+{
+    'nativeType': 'mozilla::dom::workers::Performance',
+    'headerFile': 'mozilla/dom/workers/bindings/Performance.h',
+    'workers': True,
+}],
 
 'PerformanceTiming': {
     'nativeType': 'nsPerformanceTiming',
     'headerFile': 'nsPerformance.h'
 },
 
 'PerformanceNavigation': {
     'nativeType': 'nsPerformanceNavigation',
@@ -1614,17 +1619,22 @@ DOMInterfaces = {
 
 'WorkerGlobalScope': {
     'headerFile': 'mozilla/dom/WorkerScope.h',
     'workers': True,
     'concrete': False,
     'implicitJSContext': [
         'close', 'importScripts',
     ],
-    'binaryNames': { 'console': 'getConsole', },
+    # Rename a few things so we don't have both classes and methods
+    # with the same name
+    'binaryNames': {
+        'console': 'getConsole',
+        'performance': 'getPerformance',
+    },
 },
 
 'WorkerLocation': {
     'headerFile': 'mozilla/dom/workers/bindings/Location.h',
     'workers': True,
 },
 
 'WorkerNavigator': {
--- a/dom/webidl/Performance.webidl
+++ b/dom/webidl/Performance.webidl
@@ -8,39 +8,45 @@
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 typedef double DOMHighResTimeStamp;
 typedef sequence <PerformanceEntry> PerformanceEntryList;
 
+[Exposed=(Window,Worker)]
 interface Performance {
   DOMHighResTimeStamp now();
+};
 
+[Exposed=Window]
+partial interface Performance {
   [Constant]
   readonly attribute PerformanceTiming timing;
   [Constant]
   readonly attribute PerformanceNavigation navigation;
 
   jsonifier;
 };
 
 // http://www.w3.org/TR/performance-timeline/#sec-window.performance-attribute
+[Exposed=Window]
 partial interface Performance {
   [Pref="dom.enable_resource_timing"]
   PerformanceEntryList getEntries();
   [Pref="dom.enable_resource_timing"]
   PerformanceEntryList getEntriesByType(DOMString entryType);
   [Pref="dom.enable_resource_timing"]
   PerformanceEntryList getEntriesByName(DOMString name, optional DOMString
     entryType);
 };
 
 // http://www.w3.org/TR/resource-timing/#extensions-performance-interface
+[Exposed=Window]
 partial interface Performance {
   [Pref="dom.enable_resource_timing"]
   void clearResourceTimings();
   [Pref="dom.enable_resource_timing"]
   void setResourceTimingBufferSize(unsigned long maxSize);
   [Pref="dom.enable_resource_timing"]
   attribute EventHandler onresourcetimingbufferfull;
 };
--- a/dom/webidl/WorkerGlobalScope.webidl
+++ b/dom/webidl/WorkerGlobalScope.webidl
@@ -39,9 +39,12 @@ partial interface WorkerGlobalScope {
 WorkerGlobalScope implements WindowTimers;
 WorkerGlobalScope implements WindowBase64;
 
 // Mozilla extensions
 partial interface WorkerGlobalScope {
   attribute EventHandler onclose;
 
   void dump(optional DOMString str);
+
+  // XXXbz no spec for this yet, because the webperf WG is a bit dysfunctional
+  readonly attribute Performance performance;
 };
new file mode 100644
--- /dev/null
+++ b/dom/workers/Performance.cpp
@@ -0,0 +1,43 @@
+/* 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 "Performance.h"
+#include "mozilla/dom/PerformanceBinding.h"
+
+#include "WorkerPrivate.h"
+
+BEGIN_WORKERS_NAMESPACE
+
+NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(Performance, AddRef)
+NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(Performance, Release)
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(Performance)
+
+Performance::Performance(WorkerPrivate* aWorkerPrivate)
+  : mWorkerPrivate(aWorkerPrivate)
+{
+  mWorkerPrivate->AssertIsOnWorkerThread();
+  SetIsDOMBinding();
+}
+
+Performance::~Performance()
+{
+  mWorkerPrivate->AssertIsOnWorkerThread();
+}
+
+JSObject*
+Performance::WrapObject(JSContext* aCx)
+{
+  return PerformanceBinding_workers::Wrap(aCx, this);
+}
+
+double
+Performance::Now() const
+{
+  TimeDuration duration =
+    TimeStamp::Now() - mWorkerPrivate->NowBaseTimeStamp();
+  return duration.ToMilliseconds();
+}
+
+END_WORKERS_NAMESPACE
new file mode 100644
--- /dev/null
+++ b/dom/workers/Performance.h
@@ -0,0 +1,49 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* 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_workers_performance_h__
+#define mozilla_dom_workers_performance_h__
+
+#include "nsWrapperCache.h"
+#include "js/TypeDecls.h"
+#include "Workers.h"
+#include "nsISupportsImpl.h"
+#include "nsCycleCollectionParticipant.h"
+
+BEGIN_WORKERS_NAMESPACE
+
+class WorkerPrivate;
+
+class Performance MOZ_FINAL : public nsWrapperCache
+{
+public:
+  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(Performance)
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(Performance)
+
+  Performance(WorkerPrivate* aWorkerPrivate);
+
+private:
+  ~Performance();
+
+  WorkerPrivate* mWorkerPrivate;
+
+public:
+  virtual JSObject*
+  WrapObject(JSContext* aCx) MOZ_OVERRIDE;
+
+  nsISupports*
+  GetParentObject() const
+  {
+    // There's only one global on a worker, so we don't need to specify.
+    return nullptr;
+  }
+
+  // WebIDL (public APIs)
+  double Now() const;
+};
+
+END_WORKERS_NAMESPACE
+
+#endif // mozilla_dom_workers_performance_h__
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -16,16 +16,17 @@
 #include "nsIDOMMessageEvent.h"
 #include "nsIDocument.h"
 #include "nsIDocShell.h"
 #include "nsIMemoryReporter.h"
 #include "nsIPermissionManager.h"
 #include "nsIScriptError.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIScriptSecurityManager.h"
+#include "nsPerformance.h"
 #include "nsPIDOMWindow.h"
 #include "nsITextToSubURI.h"
 #include "nsIThreadInternal.h"
 #include "nsITimer.h"
 #include "nsIURI.h"
 #include "nsIURL.h"
 #include "nsIXPConnect.h"
 
@@ -2122,21 +2123,32 @@ WorkerPrivateParent<Derived>::WorkerPriv
   }
 
   mLoadInfo.StealFrom(aLoadInfo);
 
   if (aParent) {
     aParent->AssertIsOnWorkerThread();
 
     aParent->CopyJSSettings(mJSSettings);
+
+    MOZ_ASSERT(IsDedicatedWorker());
+    mNowBaseTimeStamp = aParent->NowBaseTimeStamp();
   }
   else {
     AssertIsOnMainThread();
 
     RuntimeService::GetDefaultJSSettings(mJSSettings);
+
+    if (IsDedicatedWorker() && aLoadInfo.mWindow &&
+        aLoadInfo.mWindow->GetPerformance()) {
+      mNowBaseTimeStamp = aLoadInfo.mWindow->GetPerformance()->GetDOMTiming()->
+        GetNavigationStartTimeStamp();
+    } else {
+      mNowBaseTimeStamp = CreationTimeStamp();
+    }
   }
 }
 
 template <class Derived>
 WorkerPrivateParent<Derived>::~WorkerPrivateParent()
 {
   DropJSObjects(this);
 }
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -238,16 +238,17 @@ private:
   uint64_t mBusyCount;
   uint64_t mMessagePortSerial;
   Status mParentStatus;
   bool mParentSuspended;
   bool mIsChromeWorker;
   bool mMainThreadObjectsForgotten;
   WorkerType mWorkerType;
   TimeStamp mCreationTimeStamp;
+  TimeStamp mNowBaseTimeStamp;
 
 protected:
   // The worker is owned by its thread, which is represented here.  This is set
   // in Construct() and emptied by WorkerFinishedRunnable, and conditionally
   // traversed by the cycle collector if the busy count is zero.
   nsRefPtr<WorkerPrivate> mSelfRef;
 
   WorkerPrivateParent(JSContext* aCx, WorkerPrivate* aParent,
@@ -510,16 +511,21 @@ public:
     return mLoadInfo.mResolvedScriptURI;
   }
 
   TimeStamp CreationTimeStamp() const
   {
     return mCreationTimeStamp;
   }
 
+  TimeStamp NowBaseTimeStamp() const
+  {
+    return mNowBaseTimeStamp;
+  }
+
   nsIPrincipal*
   GetPrincipal() const
   {
     AssertIsOnMainThread();
     return mLoadInfo.mPrincipal;
   }
 
   // This method allows the principal to be retrieved off the main thread.
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -19,16 +19,17 @@
 #endif
 
 #include "Location.h"
 #include "Navigator.h"
 #include "Principal.h"
 #include "RuntimeService.h"
 #include "ScriptLoader.h"
 #include "WorkerPrivate.h"
+#include "Performance.h"
 
 #define UNWRAP_WORKER_OBJECT(Interface, obj, value)                           \
   UnwrapObject<prototypes::id::Interface##_workers,                           \
     mozilla::dom::Interface##Binding_workers::NativeType>(obj, value)
 
 using namespace mozilla;
 using namespace mozilla::dom;
 USING_WORKERS_NAMESPACE
@@ -49,22 +50,26 @@ WorkerGlobalScope::~WorkerGlobalScope()
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(WorkerGlobalScope)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WorkerGlobalScope,
                                                   DOMEventTargetHelper)
   tmp->mWorkerPrivate->AssertIsOnWorkerThread();
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsole)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPerformance)
+  // XXXbz what about mLocation and mNavigator?
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WorkerGlobalScope,
                                                 DOMEventTargetHelper)
   tmp->mWorkerPrivate->AssertIsOnWorkerThread();
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsole)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance)
+  // XXXbz what about mLocation and mNavigator?
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(WorkerGlobalScope,
                                                DOMEventTargetHelper)
   tmp->mWorkerPrivate->AssertIsOnWorkerThread();
 
   tmp->mWorkerPrivate->TraceTimeouts(aCallbacks, aClosure);
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
@@ -272,16 +277,28 @@ WorkerGlobalScope::Dump(const Optional<n
 
 #ifdef ANDROID
   __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", str.get());
 #endif
   fputs(str.get(), stdout);
   fflush(stdout);
 }
 
+Performance*
+WorkerGlobalScope::GetPerformance()
+{
+  mWorkerPrivate->AssertIsOnWorkerThread();
+
+  if (!mPerformance) {
+    mPerformance = new Performance(mWorkerPrivate);
+  }
+
+  return mPerformance;
+}
+
 DedicatedWorkerGlobalScope::DedicatedWorkerGlobalScope(WorkerPrivate* aWorkerPrivate)
 : WorkerGlobalScope(aWorkerPrivate)
 {
 }
 
 JSObject*
 DedicatedWorkerGlobalScope::WrapGlobalObject(JSContext* aCx)
 {
--- a/dom/workers/WorkerScope.h
+++ b/dom/workers/WorkerScope.h
@@ -18,23 +18,25 @@ class Function;
 } // namespace dom
 } // namespace mozilla
 
 BEGIN_WORKERS_NAMESPACE
 
 class WorkerPrivate;
 class WorkerLocation;
 class WorkerNavigator;
+class Performance;
 
 class WorkerGlobalScope : public DOMEventTargetHelper,
                           public nsIGlobalObject
 {
   nsRefPtr<Console> mConsole;
   nsRefPtr<WorkerLocation> mLocation;
   nsRefPtr<WorkerNavigator> mNavigator;
+  nsRefPtr<Performance> mPerformance;
 
 protected:
   WorkerPrivate* mWorkerPrivate;
 
   WorkerGlobalScope(WorkerPrivate* aWorkerPrivate);
   virtual ~WorkerGlobalScope();
 
 public:
@@ -110,16 +112,18 @@ public:
   Btoa(const nsAString& aBtoa, nsAString& aOutput, ErrorResult& aRv) const;
 
   IMPL_EVENT_HANDLER(online)
   IMPL_EVENT_HANDLER(offline)
   IMPL_EVENT_HANDLER(close)
 
   void
   Dump(const Optional<nsAString>& aString) const;
+
+  Performance* GetPerformance();
 };
 
 class DedicatedWorkerGlobalScope MOZ_FINAL : public WorkerGlobalScope
 {
   ~DedicatedWorkerGlobalScope() { }
 
 public:
   DedicatedWorkerGlobalScope(WorkerPrivate* aWorkerPrivate);
--- a/dom/workers/moz.build
+++ b/dom/workers/moz.build
@@ -22,16 +22,17 @@ EXPORTS.mozilla.dom.workers += [
 # Stuff needed for the bindings, not really public though.
 EXPORTS.mozilla.dom.workers.bindings += [
     'DataStore.h',
     'DataStoreCursor.h',
     'FileReaderSync.h',
     'Location.h',
     'MessagePort.h',
     'Navigator.h',
+    'Performance.h',
     'ServiceWorker.h',
     'SharedWorker.h',
     'URL.h',
     'WorkerFeature.h',
     'XMLHttpRequest.h',
     'XMLHttpRequestUpload.h',
 ]
 
@@ -39,16 +40,17 @@ SOURCES += [
     'ChromeWorkerScope.cpp',
     'DataStore.cpp',
     'DataStoreCursor.cpp',
     'File.cpp',
     'FileReaderSync.cpp',
     'Location.cpp',
     'MessagePort.cpp',
     'Navigator.cpp',
+    'Performance.cpp',
     'Principal.cpp',
     'RegisterBindings.cpp',
     'RuntimeService.cpp',
     'ScriptLoader.cpp',
     'ServiceWorker.cpp',
     'ServiceWorkerContainer.cpp',
     'ServiceWorkerEvents.cpp',
     'ServiceWorkerManager.cpp',
--- a/dom/workers/test/mochitest.ini
+++ b/dom/workers/test/mochitest.ini
@@ -74,16 +74,17 @@ support-files =
   xhr_headers_worker.js
   xhr_headers_server.sjs
   url_exceptions_worker.js
   urlSearchParams_worker.js
   subdir/relativeLoad_sub_worker.js
   subdir/relativeLoad_sub_worker2.js
   subdir/relativeLoad_sub_import.js
   test_worker_interfaces.js
+  test_worker_performance_now.js
   worker_driver.js
   worker_wrapper.js
 
 [test_404.html]
 [test_atob.html]
 [test_blobConstructor.html]
 [test_blobWorkers.html]
 [test_bug1002702.html]
@@ -147,16 +148,17 @@ skip-if = buildapp == 'b2g' || e10s # b2
 [test_threadTimeouts.html]
 [test_throwingOnerror.html]
 [test_timeoutTracing.html]
 [test_transferable.html]
 [test_url.html]
 [test_urlApi.html]
 [test_workersDisabled.html]
 [test_worker_interfaces.html]
+[test_worker_performance_now.html]
 [test_xhr.html]
 [test_xhr2.html]
 [test_xhrAbort.html]
 [test_xhr_headers.html]
 [test_xhr_implicit_cancel.html]
 [test_xhr_parameters.html]
 skip-if = buildapp == 'b2g' || e10s
 [test_xhr_parameters.js]
--- a/dom/workers/test/test_worker_interfaces.js
+++ b/dom/workers/test/test_worker_interfaces.js
@@ -97,16 +97,18 @@ var interfaceNamesInGlobalScope =
     { name: "Headers", pref: "dom.fetch.enabled" },
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "ImageData",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MessageEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MessagePort",
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    "Performance",
+// IMPORTANT: Do not change this list without review from a DOM peer!
     "Promise",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "TextDecoder",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "TextEncoder",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "XMLHttpRequest",
 // IMPORTANT: Do not change this list without review from a DOM peer!
copy from dom/workers/test/test_worker_interfaces.html
copy to dom/workers/test/test_worker_performance_now.html
--- a/dom/workers/test/test_worker_interfaces.html
+++ b/dom/workers/test/test_worker_performance_now.html
@@ -5,12 +5,12 @@
 <head>
   <title>Validate Interfaces Exposed to Workers</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="worker_driver.js"></script>
 </head>
 <body>
 <script class="testbody" type="text/javascript">
-workerTestExec("test_worker_interfaces.js");
+workerTestExec("test_worker_performance_now.js");
 </script>
 </body>
 </html>
copy from dom/tests/mochitest/general/test_performance_now.html
copy to dom/workers/test/test_worker_performance_now.js
--- a/dom/tests/mochitest/general/test_performance_now.html
+++ b/dom/workers/test/test_worker_performance_now.js
@@ -1,65 +1,54 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Test for High Resolution Timer</title>
-  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-</head>
-<body>
-  <script>
-    ok(window.performance, "Performance object should exist.");
-    ok(typeof window.performance.now == 'function', "Performance object should have a 'now' method.");
-    var n = window.performance.now(), d = Date.now();
-    ok(n >= 0, "The value of now() should be equal to or greater than 0.");
-    ok(window.performance.now() >= n, "The value of now() should monotonically increase.");
-    SimpleTest.waitForExplicitFinish();
+ok(self.performance, "Performance object should exist.");
+ok(typeof self.performance.now == 'function', "Performance object should have a 'now' method.");
+var n = self.performance.now(), d = Date.now();
+ok(n >= 0, "The value of now() should be equal to or greater than 0.");
+ok(self.performance.now() >= n, "The value of now() should monotonically increase.");
 
-    // The spec says performance.now() should have micro-second resolution, but allows 1ms if the platform doesn't support it.
-    // Our implementation does provide micro-second resolution, except for windows XP combined with some HW properties
-    // where we can't use QueryPerformanceCounters (see comments at mozilla-central/xpcom/ds/TimeStamp_windows.cpp).
-    // This XP-low-res case results in about 15ms resolutions, and can be identified when perf.now() returns only integers.
-    //
-    // Since setTimeout might return too early/late, our goal is that perf.now() changed within 2ms
-    // (or 25ms for XP-low-res), rather than specific number of setTimeout(N) invocations.
-    // See bug 749894 (intermittent failures of this test)
-    var platformPossiblyLowRes = navigator.oscpu.indexOf("Windows NT 5.1") == 0; // XP only
-    var allInts = (n % 1) == 0; // Indicator of limited HW resolution.
-    var checks = 0;
+// The spec says performance.now() should have micro-second resolution, but allows 1ms if the platform doesn't support it.
+// Our implementation does provide micro-second resolution, except for windows XP combined with some HW properties
+// where we can't use QueryPerformanceCounters (see comments at mozilla-central/xpcom/ds/TimeStamp_windows.cpp).
+// This XP-low-res case results in about 15ms resolutions, and can be identified when perf.now() returns only integers.
+//
+// Since setTimeout might return too early/late, our goal is that perf.now() changed within 2ms
+// (or 25ms for XP-low-res), rather than specific number of setTimeout(N) invocations.
+// See bug 749894 (intermittent failures of this test)
+var platformPossiblyLowRes;
+workerTestGetOSCPU(function(oscpu) {
+    platformPossiblyLowRes = oscpu.indexOf("Windows NT 5.1") == 0; // XP only
+    setTimeout(checkAfterTimeout, 1);
+});
+var allInts = (n % 1) == 0; // Indicator of limited HW resolution.
+var checks = 0;
+
+function checkAfterTimeout() {
+  checks++;
+  var d2 = Date.now();
+  var n2 = self.performance.now();
 
-    function checkAfterTimeout() {
-      checks++;
-      var d2 = Date.now();
-      var n2 = window.performance.now();
+  allInts = allInts && (n2 % 1) == 0;
+  var lowResCounter = platformPossiblyLowRes && allInts;
 
-      allInts = allInts && (n2 % 1) == 0;
-      var lowResCounter = platformPossiblyLowRes && allInts;
+  if ( n2 == n && checks < 50 && // 50 is just a failsafe. Our real goals are 2ms or 25ms.
+       ( (d2 - d) < 2 // The spec allows 1ms resolution. We allow up to measured 2ms to ellapse.
+         ||
+         lowResCounter &&
+         (d2 - d) < 25
+       )
+     ) {
+    setTimeout(checkAfterTimeout, 1);
+    return;
+  }
 
-      if ( n2 == n && checks < 50 && // 50 is just a failsafe. Our real goals are 2ms or 25ms.
-           ( (d2 - d) < 2 // The spec allows 1ms resolution. We allow up to measured 2ms to ellapse.
-             ||
-             lowResCounter &&
-             (d2 - d) < 25
-           )
-         ) {
-        setTimeout(checkAfterTimeout, 1);
-        return;
-      }
+  // Loose spec: 1ms resolution, or 15ms resolution for the XP-low-res case.
+  // We shouldn't test that dt is actually within 2/25ms since the iterations break if it isn't, and timeout could be late.
+  ok(n2 > n, "Loose - the value of now() should increase within 2ms (or 25ms if low-res counter) (delta now(): " + (n2 - n) + " ms).");
 
-      // Loose spec: 1ms resolution, or 15ms resolution for the XP-low-res case.
-      // We shouldn't test that dt is actually within 2/25ms since the iterations break if it isn't, and timeout could be late.
-      ok(n2 > n, "Loose - the value of now() should increase within 2ms (or 25ms if low-res counter) (delta now(): " + (n2 - n) + " ms).");
-
-      // Strict spec: if it's not the XP-low-res case, while the spec allows 1ms resolution, it prefers microseconds, which we provide.
-      // Since the fastest setTimeout return which I observed was ~500 microseconds, a microseconds counter should change in 1 iteretion.
-      ok(n2 > n && (lowResCounter || checks == 1),
-         "Strict - [if high-res counter] the value of now() should increase after one setTimeout (hi-res: " + (!lowResCounter) +
-                                                                                                  ", iters: " + checks +
-                                                                                                  ", dt: " + (d2 - d) +
-                                                                                                  ", now(): " + n2 + ").");
-      SimpleTest.finish();
-    };
-    setTimeout(checkAfterTimeout, 1);
-  </script>
-</body>
-</html>
+  // Strict spec: if it's not the XP-low-res case, while the spec allows 1ms resolution, it prefers microseconds, which we provide.
+  // Since the fastest setTimeout return which I observed was ~500 microseconds, a microseconds counter should change in 1 iteretion.
+  ok(n2 > n && (lowResCounter || checks == 1),
+     "Strict - [if high-res counter] the value of now() should increase after one setTimeout (hi-res: " + (!lowResCounter) +
+                                                                                              ", iters: " + checks +
+                                                                                              ", dt: " + (d2 - d) +
+                                                                                              ", now(): " + n2 + ").");
+  workerTestDone();
+};
--- a/dom/workers/test/worker_driver.js
+++ b/dom/workers/test/worker_driver.js
@@ -18,16 +18,17 @@
 //
 // There are also some functions for requesting information that requires
 // SpecialPowers or other main-thread-only resources:
 //
 //  workerTestGetPrefs() - request an array of prefs value from the main thread
 //  workerTestGetPermissions() - request an array permissions from the MT
 //  workerTestGetVersion() - request the current version string from the MT
 //  workerTestGetUserAgent() - request the user agent string from the MT
+//  workerTestGetOSCPU() - request the navigator.oscpu string from the MT
 //
 // For an example see test_worker_interfaces.html and test_worker_interfaces.js.
 
 function workerTestExec(script) {
   SimpleTest.waitForExplicitFinish();
   var worker = new Worker('worker_wrapper.js');
   worker.onmessage = function(event) {
     if (event.data.type == 'finish') {
@@ -65,16 +66,21 @@ function workerTestExec(script) {
         result: result
       });
 
     } else if (event.data.type == 'getUserAgent') {
       worker.postMessage({
         type: 'returnUserAgent',
         result: navigator.userAgent
       });
+    } else if (event.data.type == 'getOSCPU') {
+      worker.postMessage({
+        type: 'returnOSCPU',
+        result: navigator.oscpu
+      });
     }
   }
 
   worker.onerror = function(event) {
     ok(false, 'Worker had an error: ' + event.data);
     SimpleTest.finish();
   };
 
--- a/dom/workers/test/worker_wrapper.js
+++ b/dom/workers/test/worker_wrapper.js
@@ -81,16 +81,29 @@ function workerTestGetUserAgent(cb) {
     removeEventListener('message', workerTestGetUserAgentCB);
     cb(e.data.result);
   });
   postMessage({
     type: 'getUserAgent'
   });
 }
 
+function workerTestGetOSCPU(cb) {
+  addEventListener('message', function workerTestGetOSCPUCB(e) {
+    if (e.data.type !== 'returnOSCPU') {
+      return;
+    }
+    removeEventListener('message', workerTestGetOSCPUCB);
+    cb(e.data.result);
+  });
+  postMessage({
+    type: 'getOSCPU'
+  });
+}
+
 addEventListener('message', function workerWrapperOnMessage(e) {
   removeEventListener('message', workerWrapperOnMessage);
   var data = e.data;
   try {
     importScripts(data.script);
   } catch(e) {
     postMessage({
       type: 'status',