Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/jsc/bindings/ZigGlobalObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3378,8 +3378,10 @@ RefPtr<Performance> GlobalObject::performance()
{
if (!m_performance) {
auto* context = this->scriptExecutionContext();
double nanoTimeOrigin = Bun__readOriginTimerStart(this->bunVM());
auto timeOrigin = MonotonicTime::fromRawSeconds(nanoTimeOrigin / 1000.0);
// WTF MonotonicTime value at the instant bunVM()->origin_timer was captured.
// Raw variant: must ignore the fake-timers override of performance.now().
auto elapsed = Seconds { static_cast<double>(Bun__readOriginTimerRaw(this->bunVM())) / 1000000000.0 };
auto timeOrigin = MonotonicTime::now() - elapsed;
m_performance = Performance::create(context, timeOrigin);
}

Expand Down
17 changes: 4 additions & 13 deletions src/jsc/bindings/webcore/Event.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,10 @@
#include "EventPath.h"
#include "EventTarget.h"
// #include "InspectorInstrumentation.h"
// #include "Performance.h"
#include "Performance.h"
// #include "UserGestureIndicator.h"
// #include "WorkerGlobalScope.h"
#include "ZigGlobalObject.h"
#include <wtf/HexNumber.h>
#include <wtf/TZoneMallocInlines.h>
#include <wtf/text/StringBuilder.h>
Expand Down Expand Up @@ -162,18 +163,8 @@ void Event::setUnderlyingEvent(Event* underlyingEvent)

DOMHighResTimeStamp Event::timeStampForBindings(ScriptExecutionContext& context) const
{
// TODO:
return 0.0;
// Performance* performance = nullptr;
// if (is<WorkerGlobalScope>(context))
// performance = &downcast<WorkerGlobalScope>(context).performance();
// else if (auto* window = downcast<Document>(context).domWindow())
// performance = &window->performance();

// if (!performance)
// return 0;

// return std::max(performance->relativeTimeFromTimeOriginInReducedResolution(m_createTime), 0.);
auto performance = defaultGlobalObject(context.jsGlobalObject())->performance();
return std::max((m_createTime - performance->monotonicTimeOrigin()).milliseconds(), 0.0);
}

void Event::resetBeforeDispatch()
Expand Down
3 changes: 1 addition & 2 deletions src/jsc/bindings/webcore/Performance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,7 @@ DOMHighResTimeStamp Performance::now() const

DOMHighResTimeStamp Performance::timeOrigin() const
{
// return reduceTimeResolution(m_timeOrigin.approximateWallTime().secondsSinceEpoch()).milliseconds();
return m_timeOrigin.secondsSinceEpoch().milliseconds();
return Bun__readOriginTimerStart(bunVM(scriptExecutionContext()->vm()));
}

// ReducedResolutionSeconds Performance::nowInReducedResolutionSeconds() const
Expand Down
2 changes: 2 additions & 0 deletions src/jsc/bindings/webcore/Performance.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
#include <wtf/Seconds.h>

extern "C" uint64_t Bun__readOriginTimer(void*);
extern "C" uint64_t Bun__readOriginTimerRaw(void*);
extern "C" double Bun__readOriginTimerStart(void*);

namespace JSC {
Expand Down Expand Up @@ -123,6 +124,7 @@ class Performance final : public RefCounted<Performance>, public ContextDestruct

DOMHighResTimeStamp relativeTimeFromTimeOriginInReducedResolution(MonotonicTime) const;
MonotonicTime monotonicTimeFromRelativeTime(DOMHighResTimeStamp) const;
MonotonicTime monotonicTimeOrigin() const { return m_timeOrigin; }

ScriptExecutionContext* scriptExecutionContext() const final { return ContextDestructionObserver::scriptExecutionContext(); }

Expand Down
6 changes: 6 additions & 0 deletions src/jsc/virtual_machine_exports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ pub fn read_origin_timer(vm: &VirtualMachine) -> u64 {
vm.origin_timer.elapsed().as_nanos() as u64
}

// HOST_EXPORT(Bun__readOriginTimerRaw, c)
pub fn read_origin_timer_raw(vm: &VirtualMachine) -> u64 {
// Ignores the fake-timers override; used to derive Performance::m_timeOrigin.
vm.origin_timer.elapsed().as_nanos() as u64
}

// HOST_EXPORT(Bun__readOriginTimerStart, c)
pub fn read_origin_timer_start(vm: &VirtualMachine) -> f64 {
// timespce to milliseconds
Expand Down
4 changes: 2 additions & 2 deletions test/js/bun/util/inspect.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -692,7 +692,7 @@ it("CloseEvent", () => {
code: 1000,
reason: "Normal",
});
expect(Bun.inspect(closeEvent)).toMatchInlineSnapshot(`
expect(Bun.inspect(closeEvent).replace(/timeStamp: [\d.]+,/, "timeStamp: 0,")).toMatchInlineSnapshot(`
"CloseEvent {
isTrusted: false,
wasClean: false,
Expand Down Expand Up @@ -771,7 +771,7 @@ it("CustomEvent", () => {
bubbles: true,
cancelable: true,
});
expect(Bun.inspect(customEvent)).toMatchInlineSnapshot(`
expect(Bun.inspect(customEvent).replace(/timeStamp: [\d.]+,/, "timeStamp: 0,")).toMatchInlineSnapshot(`
"CustomEvent {
isTrusted: false,
detail: {
Expand Down
43 changes: 43 additions & 0 deletions test/js/web/web-globals.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,49 @@ test("MessageEvent", () => {
expect(called).toBe(true);
});

test("Event.prototype.timeStamp", async () => {
await using proc = spawn({
cmd: [
bunExe(),
"-e",
`
while (performance.now() < 10) {}
const before = performance.now();
const samples = [];
samples.push(new Event("x").timeStamp);
samples.push(new CustomEvent("x").timeStamp);
samples.push(new MessageEvent("x").timeStamp);
samples.push(new ErrorEvent("x").timeStamp);
const target = new EventTarget();
target.addEventListener("go", e => samples.push(e.timeStamp));
target.dispatchEvent(new Event("go"));
const ac = new AbortController();
ac.signal.addEventListener("abort", e => samples.push(e.timeStamp));
ac.abort();
const after = performance.now();
const ev = new Event("stable");
const first = ev.timeStamp;
while (performance.now() < after + 5) {}
console.log(JSON.stringify({ before, after, samples, first, second: ev.timeStamp }));
`,
],
env: bunEnv,
stderr: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
const { before, after, samples, first, second } = JSON.parse(stdout);
expect(samples.length).toBe(6);
// timeStamp is relative to performance.timeOrigin. 1ms of slack covers the
// one-time gap between the two clock samples that derive m_timeOrigin.
for (const ts of samples) {
expect(ts).toBeGreaterThan(before - 1);
expect(ts).toBeLessThan(after + 1);
}
expect(first).toBeGreaterThanOrEqual(samples[samples.length - 1]);
expect(second).toBe(first);
expect({ stderr, exitCode }).toEqual({ stderr: expect.any(String), exitCode: 0 });
});

it("crypto.getRandomValues", () => {
var foo = new Uint8Array(32);

Expand Down
Loading