Bug 908390 part 2. Implement performance.now() on workers. r=khuey

--HG--
rename : dom/workers/test/test_worker_interfaces.html => dom/workers/test/test_worker_performance_now.html
rename : dom/tests/mochitest/general/test_performance_now.html => dom/workers/test/test_worker_performance_now.js
This commit is contained in:
Boris Zbarsky 2014-08-29 19:50:06 -04:00
parent 3dc5b32864
commit ed56d2edc8
16 changed files with 247 additions and 2 deletions

View File

@ -959,10 +959,15 @@ DOMInterfaces = {
'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',
@ -1619,7 +1624,12 @@ DOMInterfaces = {
'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': {

View File

@ -13,9 +13,13 @@
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]
@ -25,6 +29,7 @@ interface Performance {
};
// http://www.w3.org/TR/performance-timeline/#sec-window.performance-attribute
[Exposed=Window]
partial interface Performance {
[Pref="dom.enable_resource_timing"]
PerformanceEntryList getEntries();
@ -36,6 +41,7 @@ partial interface Performance {
};
// http://www.w3.org/TR/resource-timing/#extensions-performance-interface
[Exposed=Window]
partial interface Performance {
[Pref="dom.enable_resource_timing"]
void clearResourceTimings();

View File

@ -44,4 +44,7 @@ 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;
};

View File

@ -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

49
dom/workers/Performance.h Normal file
View File

@ -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__

View File

@ -21,6 +21,7 @@
#include "nsIScriptError.h"
#include "nsIScriptGlobalObject.h"
#include "nsIScriptSecurityManager.h"
#include "nsPerformance.h"
#include "nsPIDOMWindow.h"
#include "nsITextToSubURI.h"
#include "nsIThreadInternal.h"
@ -2127,11 +2128,22 @@ WorkerPrivateParent<Derived>::WorkerPrivateParent(
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();
}
}
}

View File

@ -243,6 +243,7 @@ private:
bool mMainThreadObjectsForgotten;
WorkerType mWorkerType;
TimeStamp mCreationTimeStamp;
TimeStamp mNowBaseTimeStamp;
protected:
// The worker is owned by its thread, which is represented here. This is set
@ -515,6 +516,11 @@ public:
return mCreationTimeStamp;
}
TimeStamp NowBaseTimeStamp() const
{
return mNowBaseTimeStamp;
}
nsIPrincipal*
GetPrincipal() const
{

View File

@ -24,6 +24,7 @@
#include "RuntimeService.h"
#include "ScriptLoader.h"
#include "WorkerPrivate.h"
#include "Performance.h"
#define UNWRAP_WORKER_OBJECT(Interface, obj, value) \
UnwrapObject<prototypes::id::Interface##_workers, \
@ -54,12 +55,16 @@ 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,
@ -277,6 +282,18 @@ WorkerGlobalScope::Dump(const Optional<nsAString>& aString) const
fflush(stdout);
}
Performance*
WorkerGlobalScope::GetPerformance()
{
mWorkerPrivate->AssertIsOnWorkerThread();
if (!mPerformance) {
mPerformance = new Performance(mWorkerPrivate);
}
return mPerformance;
}
DedicatedWorkerGlobalScope::DedicatedWorkerGlobalScope(WorkerPrivate* aWorkerPrivate)
: WorkerGlobalScope(aWorkerPrivate)
{

View File

@ -23,6 +23,7 @@ BEGIN_WORKERS_NAMESPACE
class WorkerPrivate;
class WorkerLocation;
class WorkerNavigator;
class Performance;
class WorkerGlobalScope : public DOMEventTargetHelper,
public nsIGlobalObject
@ -30,6 +31,7 @@ class WorkerGlobalScope : public DOMEventTargetHelper,
nsRefPtr<Console> mConsole;
nsRefPtr<WorkerLocation> mLocation;
nsRefPtr<WorkerNavigator> mNavigator;
nsRefPtr<Performance> mPerformance;
protected:
WorkerPrivate* mWorkerPrivate;
@ -115,6 +117,8 @@ public:
void
Dump(const Optional<nsAString>& aString) const;
Performance* GetPerformance();
};
class DedicatedWorkerGlobalScope MOZ_FINAL : public WorkerGlobalScope

View File

@ -27,6 +27,7 @@ EXPORTS.mozilla.dom.workers.bindings += [
'Location.h',
'MessagePort.h',
'Navigator.h',
'Performance.h',
'ServiceWorker.h',
'SharedWorker.h',
'URL.h',
@ -44,6 +45,7 @@ SOURCES += [
'Location.cpp',
'MessagePort.cpp',
'Navigator.cpp',
'Performance.cpp',
'Principal.cpp',
'RegisterBindings.cpp',
'RuntimeService.cpp',

View File

@ -79,6 +79,7 @@ support-files =
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
@ -152,6 +153,7 @@ skip-if = buildapp == 'b2g' || e10s # b2g(test timed out, might need more time)
[test_urlApi.html]
[test_workersDisabled.html]
[test_worker_interfaces.html]
[test_worker_performance_now.html]
[test_xhr.html]
[test_xhr2.html]
[test_xhrAbort.html]

View File

@ -101,6 +101,8 @@ var interfaceNamesInGlobalScope =
"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!

View File

@ -0,0 +1,16 @@
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<!DOCTYPE HTML>
<html>
<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_performance_now.js");
</script>
</body>
</html>

View File

@ -0,0 +1,54 @@
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;
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();
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;
}
// 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 + ").");
workerTestDone();
};

View File

@ -23,6 +23,7 @@
// 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.
@ -70,6 +71,11 @@ function workerTestExec(script) {
type: 'returnUserAgent',
result: navigator.userAgent
});
} else if (event.data.type == 'getOSCPU') {
worker.postMessage({
type: 'returnOSCPU',
result: navigator.oscpu
});
}
}

View File

@ -86,6 +86,19 @@ function workerTestGetUserAgent(cb) {
});
}
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;