Skip to content

Commit

Permalink
LibWeb: Implement get_time_origin_timestamp() AO
Browse files Browse the repository at this point in the history
  • Loading branch information
tcl3 committed Jan 23, 2025
1 parent b4ebade commit a9109d9
Show file tree
Hide file tree
Showing 15 changed files with 201 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ EnvironmentSettingsSnapshot::EnvironmentSettingsSnapshot(NonnullOwnPtr<JS::Execu
, m_url(serialized_settings.api_base_url)
, m_origin(serialized_settings.origin)
, m_policy_container(serialized_settings.policy_container)
, m_time_origin(serialized_settings.time_origin)
{
// Why can't we put these in the init list? grandparent class members are strange it seems
this->id = serialized_settings.id;
this->creation_url = serialized_settings.creation_url;
this->top_level_creation_url = serialized_settings.top_level_creation_url;
this->top_level_creation_url = serialized_settings.top_level_creation_url;
}

// Out of line to ensure this class has a key function
Expand Down
2 changes: 2 additions & 0 deletions Libraries/LibWeb/HTML/Scripting/EnvironmentSettingsSnapshot.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,14 @@ class EnvironmentSettingsSnapshot final
URL::Origin origin() const override { return m_origin; }
PolicyContainer policy_container() const override { return m_policy_container; }
CanUseCrossOriginIsolatedAPIs cross_origin_isolated_capability() const override { return CanUseCrossOriginIsolatedAPIs::No; }
double time_origin() const override { return m_time_origin; }

private:
String m_api_url_character_encoding;
URL::URL m_url;
URL::Origin m_origin;
HTML::PolicyContainer m_policy_container;
double m_time_origin { 0 };
};

}
3 changes: 3 additions & 0 deletions Libraries/LibWeb/HTML/Scripting/Environments.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ struct EnvironmentSettingsObject : public Environment {
// https://html.spec.whatwg.org/multipage/webappapis.html#concept-settings-object-cross-origin-isolated-capability
virtual CanUseCrossOriginIsolatedAPIs cross_origin_isolated_capability() const = 0;

// https://html.spec.whatwg.org/multipage/webappapis.html#concept-settings-object-time-origin
virtual double time_origin() const = 0;

URL::URL parse_url(StringView);
URL::URL encoding_parse_url(StringView);
Optional<String> encoding_parse_and_serialize_url(StringView);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ ErrorOr<void> encode(Encoder& encoder, Web::HTML::SerializedEnvironmentSettingsO
TRY(encoder.encode(object.origin));
TRY(encoder.encode(object.policy_container));
TRY(encoder.encode(object.cross_origin_isolated_capability));
TRY(encoder.encode(object.time_origin));

return {};
}
Expand All @@ -40,6 +41,7 @@ ErrorOr<Web::HTML::SerializedEnvironmentSettingsObject> decode(Decoder& decoder)
object.origin = TRY(decoder.decode<URL::Origin>());
object.policy_container = TRY(decoder.decode<Web::HTML::PolicyContainer>());
object.cross_origin_isolated_capability = TRY(decoder.decode<Web::HTML::CanUseCrossOriginIsolatedAPIs>());
object.time_origin = TRY(decoder.decode<double>());

return object;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ struct SerializedEnvironmentSettingsObject {
URL::Origin origin;
PolicyContainer policy_container;
CanUseCrossOriginIsolatedAPIs cross_origin_isolated_capability;
double time_origin;
};

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,13 @@ PolicyContainer WindowEnvironmentSettingsObject::policy_container() const
return m_window->associated_document().policy_container();
}

// https://html.spec.whatwg.org/multipage/nav-history-apis.html#script-settings-for-window-objects:concept-settings-object-time-origin
double WindowEnvironmentSettingsObject::time_origin() const
{
// Return window's associated Document's load timing info's navigation start time.
return m_window->associated_document().load_timing_info().navigation_start_time;
}

// https://html.spec.whatwg.org/multipage/window-object.html#script-settings-for-window-objects:concept-settings-object-cross-origin-isolated-capability
CanUseCrossOriginIsolatedAPIs WindowEnvironmentSettingsObject::cross_origin_isolated_capability() const
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class WindowEnvironmentSettingsObject final : public EnvironmentSettingsObject {
virtual URL::Origin origin() const override;
virtual PolicyContainer policy_container() const override;
virtual CanUseCrossOriginIsolatedAPIs cross_origin_isolated_capability() const override;
virtual double time_origin() const override;

private:
WindowEnvironmentSettingsObject(Window&, NonnullOwnPtr<JS::ExecutionContext>);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ GC_DEFINE_ALLOCATOR(WorkerEnvironmentSettingsObject);
// https://html.spec.whatwg.org/multipage/workers.html#set-up-a-worker-environment-settings-object
GC::Ref<WorkerEnvironmentSettingsObject> WorkerEnvironmentSettingsObject::setup(GC::Ref<Page> page, NonnullOwnPtr<JS::ExecutionContext> execution_context, SerializedEnvironmentSettingsObject const& outside_settings, HighResolutionTime::DOMHighResTimeStamp unsafe_worker_creation_time)
{
(void)unsafe_worker_creation_time;

// 1. Let inherited origin be outside settings's origin.
auto inherited_origin = outside_settings.origin;

Expand All @@ -32,7 +30,7 @@ GC::Ref<WorkerEnvironmentSettingsObject> WorkerEnvironmentSettingsObject::setup(

// 4. Let settings object be a new environment settings object whose algorithms are defined as follows:
// NOTE: See the functions defined for this class.
auto settings_object = realm->create<WorkerEnvironmentSettingsObject>(move(execution_context), worker);
auto settings_object = realm->create<WorkerEnvironmentSettingsObject>(move(execution_context), worker, unsafe_worker_creation_time);
settings_object->target_browsing_context = nullptr;
settings_object->m_origin = move(inherited_origin);

Expand Down Expand Up @@ -80,6 +78,12 @@ CanUseCrossOriginIsolatedAPIs WorkerEnvironmentSettingsObject::cross_origin_isol
return CanUseCrossOriginIsolatedAPIs::No;
}

double WorkerEnvironmentSettingsObject::time_origin() const
{
// Return the result of coarsening unsafeWorkerCreationTime with worker global scope's cross-origin isolated capability.
return HighResolutionTime::coarsen_time(m_unsafe_worker_creation_time, cross_origin_isolated_capability() == CanUseCrossOriginIsolatedAPIs::Yes);
}

void WorkerEnvironmentSettingsObject::visit_edges(JS::Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ class WorkerEnvironmentSettingsObject final
GC_DECLARE_ALLOCATOR(WorkerEnvironmentSettingsObject);

public:
WorkerEnvironmentSettingsObject(NonnullOwnPtr<JS::ExecutionContext> execution_context, GC::Ref<WorkerGlobalScope> global_scope)
WorkerEnvironmentSettingsObject(NonnullOwnPtr<JS::ExecutionContext> execution_context, GC::Ref<WorkerGlobalScope> global_scope, HighResolutionTime::DOMHighResTimeStamp unsafe_worker_creation_time)
: EnvironmentSettingsObject(move(execution_context))
, m_global_scope(global_scope)
, m_unsafe_worker_creation_time(unsafe_worker_creation_time)
{
}

Expand All @@ -34,6 +35,7 @@ class WorkerEnvironmentSettingsObject final
URL::Origin origin() const override;
PolicyContainer policy_container() const override;
CanUseCrossOriginIsolatedAPIs cross_origin_isolated_capability() const override;
double time_origin() const override;

private:
virtual void visit_edges(JS::Cell::Visitor&) override;
Expand All @@ -42,6 +44,8 @@ class WorkerEnvironmentSettingsObject final
URL::Origin m_origin;

GC::Ref<WorkerGlobalScope> m_global_scope;

HighResolutionTime::DOMHighResTimeStamp m_unsafe_worker_creation_time { 0 };
};

}
16 changes: 8 additions & 8 deletions Libraries/LibWeb/HighResolutionTime/TimeOrigin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,31 +14,30 @@ namespace Web::HighResolutionTime {
// https://w3c.github.io/hr-time/#dfn-get-time-origin-timestamp
DOMHighResTimeStamp get_time_origin_timestamp(JS::Object const& global)
{
(void)global;

// To get time origin timestamp, given a global object global, run the following steps, which return a duration:
// FIXME: 1. Let timeOrigin be global's relevant settings object's time origin.
auto time_origin = 0;
// 1. Let timeOrigin be global's relevant settings object's time origin.
auto time_origin = HTML::relevant_principal_settings_object(global).time_origin();

// Each group of environment settings objects that could possibly communicate in any way
// has an estimated monotonic time of the Unix epoch, a moment on the monotonic clock,
// whose value is initialized by the following steps:

// !. Let wall time be the wall clock's unsafe current time.
struct timeval tv;
gettimeofday(&tv, nullptr);
auto wall_time = tv.tv_sec * 1000.0 + tv.tv_usec / 1000.0;
// struct timeval tv;
// gettimeofday(&tv, nullptr);
auto wall_time = UnixDateTime::now();

// 2. Let monotonic time be the monotonic clock's unsafe current time.
auto monotonic_time = unsafe_shared_current_time();

// 3. Let epoch time be monotonic time - (wall time - Unix epoch)
auto epoch_time = monotonic_time - (wall_time - 0);
auto epoch_time = monotonic_time - (wall_time.nanoseconds_since_epoch() / 1e6);

// 4. Initialize the estimated monotonic time of the Unix epoch to the result of calling coarsen time with epoch time
auto estimated_monotonic_time = coarsen_time(epoch_time);

// 2. Return the duration from the estimated monotonic time of the Unix epoch to timeOrigin.
// dbgln("Result: {}, Estimated monotonic time: {}, time origin: {}, monotonic time: {}, wall time: {}", estimated_monotonic_time - time_origin, estimated_monotonic_time, time_origin, monotonic_time, wall_time.nanoseconds_since_epoch() / 1e6);
return estimated_monotonic_time - time_origin;
}

Expand All @@ -55,6 +54,7 @@ DOMHighResTimeStamp current_high_resolution_time(JS::Object const& global)
{
// The current high resolution time given a global object current global must return the result
// of relative high resolution time given unsafe shared current time and current global.
// dbgln("High resolution time: {}", HighResolutionTime::relative_high_resolution_time(HighResolutionTime::unsafe_shared_current_time(), global));
return HighResolutionTime::relative_high_resolution_time(HighResolutionTime::unsafe_shared_current_time(), global);
}

Expand Down
4 changes: 2 additions & 2 deletions Libraries/LibWeb/UserTiming/PerformanceMark.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <LibWeb/Bindings/PerformanceMarkPrototype.h>
#include <LibWeb/HTML/StructuredSerialize.h>
#include <LibWeb/HTML/Window.h>
#include <LibWeb/HighResolutionTime/Performance.h>
#include <LibWeb/HighResolutionTime/TimeOrigin.h>
#include <LibWeb/NavigationTiming/EntryNames.h>
#include <LibWeb/PerformanceTimeline/EntryTypes.h>
Expand Down Expand Up @@ -68,8 +69,7 @@ WebIDL::ExceptionOr<GC::Ref<PerformanceMark>> PerformanceMark::construct_impl(JS
}
// 2. Otherwise, set it to the value that would be returned by the Performance object's now() method.
else {
// FIXME: Performance#now doesn't currently use TimeOrigin's functions, update this and Performance#now to match Performance#now's specification.
start_time = HighResolutionTime::unsafe_shared_current_time();
start_time = HighResolutionTime::current_high_resolution_time(current_principal_global_object);
}

// 6. Set entry's duration attribute to 0.
Expand Down
27 changes: 27 additions & 0 deletions Tests/LibWeb/Text/expected/wpt-import/user-timing/mark.any.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
Harness status: OK

Found 22 tests

22 Pass
Pass Entry 0 is properly created
Pass Entry 1 is properly created
Pass Entry 0 has the proper name
Pass Entry 0 startTime is approximately correct (up to 20ms difference allowed)
Pass Entry 0 has the proper entryType
Pass Entry 0 duration == 0
Pass getEntriesByName("mark", "mark")[0] returns an object containing a "mark" mark
Pass The mark returned by getEntriesByName("mark", "mark")[0] matches the mark returned by getEntriesByName("mark")[0]
Pass getEntries()[0] returns an object containing a "mark" mark
Pass The mark returned by getEntries()[0] matches the mark returned by getEntriesByName("mark")[0]
Pass getEntriesByType("mark")[0] returns an object containing a "mark" mark
Pass The mark returned by getEntriesByType("mark")[0] matches the mark returned by getEntriesByName("mark")[0]
Pass Entry 1 has the proper name
Pass Entry 1 startTime is approximately correct (up to 20ms difference allowed)
Pass Entry 1 has the proper entryType
Pass Entry 1 duration == 0
Pass getEntriesByName("mark", "mark")[1] returns an object containing a "mark" mark
Pass The mark returned by getEntriesByName("mark", "mark")[1] matches the mark returned by getEntriesByName("mark")[1]
Pass getEntries()[1] returns an object containing a "mark" mark
Pass The mark returned by getEntries()[1] matches the mark returned by getEntriesByName("mark")[1]
Pass getEntriesByType("mark")[1] returns an object containing a "mark" mark
Pass The mark returned by getEntriesByType("mark")[1] matches the mark returned by getEntriesByName("mark")[1]
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@ Harness status: OK

Found 8 tests

7 Pass
1 Fail
8 Pass
Pass Measure of navigationStart to now should be positive value.
Pass Measure of navigationStart to loadEventEnd should be positive value.
Pass Measure of current mark to navigationStart should be negative value.
Fail loadTime plus loadEventEnd to a mark "a" should equal to navigationStart to "a".
Pass loadTime plus loadEventEnd to a mark "a" should equal to navigationStart to "a".
Pass Second measure of current mark to navigationStart should be negative value.
Pass Measures of loadTime should have same duration.
Pass Measure from domComplete event to most recent mark "a" should have longer duration.
Expand Down
15 changes: 15 additions & 0 deletions Tests/LibWeb/Text/input/wpt-import/user-timing/mark.any.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!doctype html>
<meta charset=utf-8>

<script>
self.GLOBAL = {
isWindow: function() { return true; },
isWorker: function() { return false; },
isShadowRealm: function() { return false; },
};
</script>
<script src="../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script>

<div id=log></div>
<script src="../user-timing/mark.any.js"></script>
118 changes: 118 additions & 0 deletions Tests/LibWeb/Text/input/wpt-import/user-timing/mark.any.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// test data
var testThreshold = 20;

var expectedTimes = new Array();

function match_entries(entries, index)
{
var entry = entries[index];
var match = self.performance.getEntriesByName("mark")[index];
assert_equals(entry.name, match.name, "entry.name");
assert_equals(entry.startTime, match.startTime, "entry.startTime");
assert_equals(entry.entryType, match.entryType, "entry.entryType");
assert_equals(entry.duration, match.duration, "entry.duration");
}

function filter_entries_by_type(entryList, entryType)
{
var testEntries = new Array();

// filter entryList
for (var i in entryList)
{
if (entryList[i].entryType == entryType)
{
testEntries.push(entryList[i]);
}
}

return testEntries;
}

test(function () {
// create first mark
self.performance.mark("mark");

expectedTimes[0] = self.performance.now();

const entries = self.performance.getEntriesByName("mark");
assert_equals(entries.length, 1);
}, "Entry 0 is properly created");

test(function () {
// create second, duplicate mark
self.performance.mark("mark");

expectedTimes[1] = self.performance.now();

const entries = self.performance.getEntriesByName("mark");
assert_equals(entries.length, 2);

}, "Entry 1 is properly created");

function test_mark(index) {
test(function () {
const entries = self.performance.getEntriesByName("mark");
assert_equals(entries[index].name, "mark", "Entry has the proper name");
}, "Entry " + index + " has the proper name");

test(function () {
const entries = self.performance.getEntriesByName("mark");
assert_approx_equals(entries[index].startTime, expectedTimes[index], testThreshold);
}, "Entry " + index + " startTime is approximately correct (up to " + testThreshold +
"ms difference allowed)");

test(function () {
const entries = self.performance.getEntriesByName("mark");
assert_equals(entries[index].entryType, "mark");
}, "Entry " + index + " has the proper entryType");

test(function () {
const entries = self.performance.getEntriesByName("mark");
assert_equals(entries[index].duration, 0);
}, "Entry " + index + " duration == 0");

test(function () {
const entries = self.performance.getEntriesByName("mark", "mark");
assert_equals(entries[index].name, "mark");
}, "getEntriesByName(\"mark\", \"mark\")[" + index + "] returns an " +
"object containing a \"mark\" mark");

test(function () {
const entries = self.performance.getEntriesByName("mark", "mark");
match_entries(entries, index);
}, "The mark returned by getEntriesByName(\"mark\", \"mark\")[" + index
+ "] matches the mark returned by " +
"getEntriesByName(\"mark\")[" + index + "]");

test(function () {
const entries = filter_entries_by_type(self.performance.getEntries(), "mark");
assert_equals(entries[index].name, "mark");
}, "getEntries()[" + index + "] returns an " +
"object containing a \"mark\" mark");

test(function () {
const entries = filter_entries_by_type(self.performance.getEntries(), "mark");
match_entries(entries, index);
}, "The mark returned by getEntries()[" + index
+ "] matches the mark returned by " +
"getEntriesByName(\"mark\")[" + index + "]");

test(function () {
const entries = self.performance.getEntriesByType("mark");
assert_equals(entries[index].name, "mark");
}, "getEntriesByType(\"mark\")[" + index + "] returns an " +
"object containing a \"mark\" mark");

test(function () {
const entries = self.performance.getEntriesByType("mark");
match_entries(entries, index);
}, "The mark returned by getEntriesByType(\"mark\")[" + index
+ "] matches the mark returned by " +
"getEntriesByName(\"mark\")[" + index + "]");

}

for (var i = 0; i < expectedTimes.length; i++) {
test_mark(i);
}

0 comments on commit a9109d9

Please sign in to comment.