diff --git a/Libraries/LibWeb/HTML/Scripting/EnvironmentSettingsSnapshot.cpp b/Libraries/LibWeb/HTML/Scripting/EnvironmentSettingsSnapshot.cpp index 7ea34b4b3cb6..c0998753fd89 100644 --- a/Libraries/LibWeb/HTML/Scripting/EnvironmentSettingsSnapshot.cpp +++ b/Libraries/LibWeb/HTML/Scripting/EnvironmentSettingsSnapshot.cpp @@ -16,12 +16,12 @@ EnvironmentSettingsSnapshot::EnvironmentSettingsSnapshot(NonnullOwnPtrid = 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 diff --git a/Libraries/LibWeb/HTML/Scripting/EnvironmentSettingsSnapshot.h b/Libraries/LibWeb/HTML/Scripting/EnvironmentSettingsSnapshot.h index 9a32fca23dee..bb0e97e68b71 100644 --- a/Libraries/LibWeb/HTML/Scripting/EnvironmentSettingsSnapshot.h +++ b/Libraries/LibWeb/HTML/Scripting/EnvironmentSettingsSnapshot.h @@ -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 }; }; } diff --git a/Libraries/LibWeb/HTML/Scripting/Environments.h b/Libraries/LibWeb/HTML/Scripting/Environments.h index 532d1dc36d7c..4757942f33a6 100644 --- a/Libraries/LibWeb/HTML/Scripting/Environments.h +++ b/Libraries/LibWeb/HTML/Scripting/Environments.h @@ -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 encoding_parse_and_serialize_url(StringView); diff --git a/Libraries/LibWeb/HTML/Scripting/SerializedEnvironmentSettingsObject.cpp b/Libraries/LibWeb/HTML/Scripting/SerializedEnvironmentSettingsObject.cpp index 02acfefc3439..1365a06f180b 100644 --- a/Libraries/LibWeb/HTML/Scripting/SerializedEnvironmentSettingsObject.cpp +++ b/Libraries/LibWeb/HTML/Scripting/SerializedEnvironmentSettingsObject.cpp @@ -22,6 +22,7 @@ ErrorOr 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 {}; } @@ -40,6 +41,7 @@ ErrorOr decode(Decoder& decoder) object.origin = TRY(decoder.decode()); object.policy_container = TRY(decoder.decode()); object.cross_origin_isolated_capability = TRY(decoder.decode()); + object.time_origin = TRY(decoder.decode()); return object; } diff --git a/Libraries/LibWeb/HTML/Scripting/SerializedEnvironmentSettingsObject.h b/Libraries/LibWeb/HTML/Scripting/SerializedEnvironmentSettingsObject.h index 6e107490bd17..fb03d051d670 100644 --- a/Libraries/LibWeb/HTML/Scripting/SerializedEnvironmentSettingsObject.h +++ b/Libraries/LibWeb/HTML/Scripting/SerializedEnvironmentSettingsObject.h @@ -30,6 +30,7 @@ struct SerializedEnvironmentSettingsObject { URL::Origin origin; PolicyContainer policy_container; CanUseCrossOriginIsolatedAPIs cross_origin_isolated_capability; + double time_origin; }; } diff --git a/Libraries/LibWeb/HTML/Scripting/WindowEnvironmentSettingsObject.cpp b/Libraries/LibWeb/HTML/Scripting/WindowEnvironmentSettingsObject.cpp index 58de96d361e8..6e4f7b9f28db 100644 --- a/Libraries/LibWeb/HTML/Scripting/WindowEnvironmentSettingsObject.cpp +++ b/Libraries/LibWeb/HTML/Scripting/WindowEnvironmentSettingsObject.cpp @@ -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 { diff --git a/Libraries/LibWeb/HTML/Scripting/WindowEnvironmentSettingsObject.h b/Libraries/LibWeb/HTML/Scripting/WindowEnvironmentSettingsObject.h index 030cd8d8fc00..8dab2734d658 100644 --- a/Libraries/LibWeb/HTML/Scripting/WindowEnvironmentSettingsObject.h +++ b/Libraries/LibWeb/HTML/Scripting/WindowEnvironmentSettingsObject.h @@ -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); diff --git a/Libraries/LibWeb/HTML/Scripting/WorkerEnvironmentSettingsObject.cpp b/Libraries/LibWeb/HTML/Scripting/WorkerEnvironmentSettingsObject.cpp index b23596ed5278..d6f6aef0d86d 100644 --- a/Libraries/LibWeb/HTML/Scripting/WorkerEnvironmentSettingsObject.cpp +++ b/Libraries/LibWeb/HTML/Scripting/WorkerEnvironmentSettingsObject.cpp @@ -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::setup(GC::Ref page, NonnullOwnPtr 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; @@ -32,7 +30,7 @@ GC::Ref 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(move(execution_context), worker); + auto settings_object = realm->create(move(execution_context), worker, unsafe_worker_creation_time); settings_object->target_browsing_context = nullptr; settings_object->m_origin = move(inherited_origin); @@ -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); diff --git a/Libraries/LibWeb/HTML/Scripting/WorkerEnvironmentSettingsObject.h b/Libraries/LibWeb/HTML/Scripting/WorkerEnvironmentSettingsObject.h index ade515050d5f..a160a8a31732 100644 --- a/Libraries/LibWeb/HTML/Scripting/WorkerEnvironmentSettingsObject.h +++ b/Libraries/LibWeb/HTML/Scripting/WorkerEnvironmentSettingsObject.h @@ -18,9 +18,10 @@ class WorkerEnvironmentSettingsObject final GC_DECLARE_ALLOCATOR(WorkerEnvironmentSettingsObject); public: - WorkerEnvironmentSettingsObject(NonnullOwnPtr execution_context, GC::Ref global_scope) + WorkerEnvironmentSettingsObject(NonnullOwnPtr execution_context, GC::Ref 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) { } @@ -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; @@ -42,6 +44,8 @@ class WorkerEnvironmentSettingsObject final URL::Origin m_origin; GC::Ref m_global_scope; + + HighResolutionTime::DOMHighResTimeStamp m_unsafe_worker_creation_time { 0 }; }; } diff --git a/Libraries/LibWeb/HighResolutionTime/TimeOrigin.cpp b/Libraries/LibWeb/HighResolutionTime/TimeOrigin.cpp index e1ad35dcc85d..a297a3aac618 100644 --- a/Libraries/LibWeb/HighResolutionTime/TimeOrigin.cpp +++ b/Libraries/LibWeb/HighResolutionTime/TimeOrigin.cpp @@ -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; } @@ -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); } diff --git a/Libraries/LibWeb/UserTiming/PerformanceMark.cpp b/Libraries/LibWeb/UserTiming/PerformanceMark.cpp index 22683c14cd77..91d2487839d6 100644 --- a/Libraries/LibWeb/UserTiming/PerformanceMark.cpp +++ b/Libraries/LibWeb/UserTiming/PerformanceMark.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -68,8 +69,7 @@ WebIDL::ExceptionOr> 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. diff --git a/Tests/LibWeb/Text/expected/wpt-import/user-timing/mark.any.txt b/Tests/LibWeb/Text/expected/wpt-import/user-timing/mark.any.txt new file mode 100644 index 000000000000..e568181a91b2 --- /dev/null +++ b/Tests/LibWeb/Text/expected/wpt-import/user-timing/mark.any.txt @@ -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] \ No newline at end of file diff --git a/Tests/LibWeb/Text/expected/wpt-import/user-timing/measure_associated_with_navigation_timing.txt b/Tests/LibWeb/Text/expected/wpt-import/user-timing/measure_associated_with_navigation_timing.txt index b936072438a3..37ac52c33911 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/user-timing/measure_associated_with_navigation_timing.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/user-timing/measure_associated_with_navigation_timing.txt @@ -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. diff --git a/Tests/LibWeb/Text/input/wpt-import/user-timing/mark.any.html b/Tests/LibWeb/Text/input/wpt-import/user-timing/mark.any.html new file mode 100644 index 000000000000..1753d738e6f0 --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/user-timing/mark.any.html @@ -0,0 +1,15 @@ + + + + + + + +
+ diff --git a/Tests/LibWeb/Text/input/wpt-import/user-timing/mark.any.js b/Tests/LibWeb/Text/input/wpt-import/user-timing/mark.any.js new file mode 100644 index 000000000000..7e814d2074ca --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/user-timing/mark.any.js @@ -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); +}