This commit is contained in:
Aniebiet Afia 2025-06-03 02:13:23 +01:00 committed by GitHub
commit 80bf6cddd8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 340 additions and 27 deletions

View file

@ -1086,6 +1086,11 @@ impl Document {
// https://html.spec.whatwg.org/multipage/#current-document-readiness // https://html.spec.whatwg.org/multipage/#current-document-readiness
pub(crate) fn set_ready_state(&self, state: DocumentReadyState, can_gc: CanGc) { pub(crate) fn set_ready_state(&self, state: DocumentReadyState, can_gc: CanGc) {
let window = self.window();
let performance = window.Performance();
let now = (*performance.Now()).floor() as u64;
let timing = performance.Timing();
match state { match state {
DocumentReadyState::Loading => { DocumentReadyState::Loading => {
if self.window().is_top_level() { if self.window().is_top_level() {
@ -1095,6 +1100,11 @@ impl Document {
)); ));
self.send_to_embedder(EmbedderMsg::Status(self.webview_id(), None)); self.send_to_embedder(EmbedderMsg::Status(self.webview_id(), None));
} }
timing.update_dom_loading(now);
},
DocumentReadyState::Interactive => {
update_with_current_instant(&self.dom_interactive);
timing.update_dom_interactive(now);
}, },
DocumentReadyState::Complete => { DocumentReadyState::Complete => {
if self.window().is_top_level() { if self.window().is_top_level() {
@ -1104,8 +1114,8 @@ impl Document {
)); ));
} }
update_with_current_instant(&self.dom_complete); update_with_current_instant(&self.dom_complete);
timing.update_dom_complete(now);
}, },
DocumentReadyState::Interactive => update_with_current_instant(&self.dom_interactive),
}; };
self.ready_state.set(state); self.ready_state.set(state);
@ -2987,6 +2997,10 @@ impl Document {
if document.browsing_context().is_none() { if document.browsing_context().is_none() {
return; return;
} }
let performance = window.Performance();
let timing = performance.Timing();
let event = Event::new( let event = Event::new(
window.upcast(), window.upcast(),
atom!("load"), atom!("load"),
@ -2996,12 +3010,16 @@ impl Document {
); );
event.set_trusted(true); event.set_trusted(true);
let start_time = (*performance.Now()).floor() as u64;
timing.update_load_event_start(start_time);
// http://w3c.github.io/navigation-timing/#widl-PerformanceNavigationTiming-loadEventStart // http://w3c.github.io/navigation-timing/#widl-PerformanceNavigationTiming-loadEventStart
update_with_current_instant(&document.load_event_start); update_with_current_instant(&document.load_event_start);
debug!("About to dispatch load for {:?}", document.url()); debug!("About to dispatch load for {:?}", document.url());
window.dispatch_event_with_target_override(&event, CanGc::note()); window.dispatch_event_with_target_override(&event, CanGc::note());
timing.update_load_event_end((*performance.Now()).floor() as u64);
// http://w3c.github.io/navigation-timing/#widl-PerformanceNavigationTiming-loadEventEnd // http://w3c.github.io/navigation-timing/#widl-PerformanceNavigationTiming-loadEventEnd
update_with_current_instant(&document.load_event_end); update_with_current_instant(&document.load_event_end);
@ -3243,20 +3261,25 @@ impl Document {
"Complete before DOMContentLoaded?" "Complete before DOMContentLoaded?"
); );
update_with_current_instant(&self.dom_content_loaded_event_start);
// Step 4.1. // Step 4.1.
let document = Trusted::new(self); let document = Trusted::new(self);
self.owner_global() self.owner_global()
.task_manager() .task_manager()
.dom_manipulation_task_source() .dom_manipulation_task_source()
.queue( .queue(task!(fire_dom_content_loaded_event: move || {
task!(fire_dom_content_loaded_event: move || { let document = document.root();
let document = document.root();
document.upcast::<EventTarget>().fire_bubbling_event(atom!("DOMContentLoaded"), CanGc::note()); let window = document.window();
update_with_current_instant(&document.dom_content_loaded_event_end); let performance = window.Performance();
}) let timing = performance.Timing();
);
update_with_current_instant(&document.dom_content_loaded_event_start);
timing.update_dom_content_loaded_event_start((*performance.Now()).floor() as u64);
document.upcast::<EventTarget>().fire_bubbling_event(atom!("DOMContentLoaded"), CanGc::note());
timing.update_dom_content_loaded_event_end((*performance.Now()).floor() as u64);
update_with_current_instant(&document.dom_content_loaded_event_end);
}));
// html parsing has finished - set dom content loaded // html parsing has finished - set dom content loaded
self.interactive_time self.interactive_time

View file

@ -484,6 +484,7 @@ pub(crate) mod performanceobserver;
pub(crate) mod performanceobserverentrylist; pub(crate) mod performanceobserverentrylist;
pub(crate) mod performancepainttiming; pub(crate) mod performancepainttiming;
pub(crate) mod performanceresourcetiming; pub(crate) mod performanceresourcetiming;
pub(crate) mod performancetiming;
pub(crate) mod permissions; pub(crate) mod permissions;
pub(crate) mod permissionstatus; pub(crate) mod permissionstatus;
pub(crate) mod plugin; pub(crate) mod plugin;

View file

@ -19,7 +19,7 @@ use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::num::Finite; use crate::dom::bindings::num::Finite;
use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object}; use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object};
use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::bindings::str::DOMString; use crate::dom::bindings::str::DOMString;
use crate::dom::eventtarget::EventTarget; use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope; use crate::dom::globalscope::GlobalScope;
@ -27,8 +27,8 @@ use crate::dom::performanceentry::PerformanceEntry;
use crate::dom::performancemark::PerformanceMark; use crate::dom::performancemark::PerformanceMark;
use crate::dom::performancemeasure::PerformanceMeasure; use crate::dom::performancemeasure::PerformanceMeasure;
use crate::dom::performancenavigation::PerformanceNavigation; use crate::dom::performancenavigation::PerformanceNavigation;
use crate::dom::performancenavigationtiming::PerformanceNavigationTiming;
use crate::dom::performanceobserver::PerformanceObserver as DOMPerformanceObserver; use crate::dom::performanceobserver::PerformanceObserver as DOMPerformanceObserver;
use crate::dom::performancetiming::PerformanceTiming;
use crate::dom::window::Window; use crate::dom::window::Window;
use crate::script_runtime::CanGc; use crate::script_runtime::CanGc;
@ -148,10 +148,23 @@ pub(crate) struct Performance {
resource_timing_buffer_current_size: Cell<usize>, resource_timing_buffer_current_size: Cell<usize>,
resource_timing_buffer_pending_full_event: Cell<bool>, resource_timing_buffer_pending_full_event: Cell<bool>,
resource_timing_secondary_entries: DomRefCell<VecDeque<DomRoot<PerformanceEntry>>>, resource_timing_secondary_entries: DomRefCell<VecDeque<DomRoot<PerformanceEntry>>>,
timing: Dom<PerformanceTiming>,
navigation: Dom<PerformanceNavigation>,
} }
impl Performance { impl Performance {
fn new_inherited(time_origin: CrossProcessInstant) -> Performance { fn time_origin_to_millis(time_origin: CrossProcessInstant) -> u64 {
(time_origin - CrossProcessInstant::epoch()).whole_milliseconds() as u64
}
fn new_inherited(
global: &GlobalScope,
time_origin: CrossProcessInstant,
can_gc: CanGc,
) -> Performance {
let nav_start = Self::time_origin_to_millis(time_origin);
let timing = PerformanceTiming::new(global, nav_start, can_gc);
let navigation = PerformanceNavigation::new(global, can_gc);
Performance { Performance {
eventtarget: EventTarget::new_inherited(), eventtarget: EventTarget::new_inherited(),
buffer: DomRefCell::new(PerformanceEntryList::new(Vec::new())), buffer: DomRefCell::new(PerformanceEntryList::new(Vec::new())),
@ -162,6 +175,8 @@ impl Performance {
resource_timing_buffer_current_size: Cell::new(0), resource_timing_buffer_current_size: Cell::new(0),
resource_timing_buffer_pending_full_event: Cell::new(false), resource_timing_buffer_pending_full_event: Cell::new(false),
resource_timing_secondary_entries: DomRefCell::new(VecDeque::new()), resource_timing_secondary_entries: DomRefCell::new(VecDeque::new()),
timing: Dom::from_ref(&*timing),
navigation: Dom::from_ref(&*navigation),
} }
} }
@ -171,7 +186,7 @@ impl Performance {
can_gc: CanGc, can_gc: CanGc,
) -> DomRoot<Performance> { ) -> DomRoot<Performance> {
reflect_dom_object( reflect_dom_object(
Box::new(Performance::new_inherited(navigation_start)), Box::new(Performance::new_inherited(global, navigation_start, can_gc)),
global, global,
can_gc, can_gc,
) )
@ -433,21 +448,13 @@ impl Performance {
impl PerformanceMethods<crate::DomTypeHolder> for Performance { impl PerformanceMethods<crate::DomTypeHolder> for Performance {
// FIXME(avada): this should be deprecated in the future, but some sites still use it // FIXME(avada): this should be deprecated in the future, but some sites still use it
// https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview.html#performance-timing-attribute // https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview.html#performance-timing-attribute
fn Timing(&self) -> DomRoot<PerformanceNavigationTiming> { fn Timing(&self) -> DomRoot<PerformanceTiming> {
let entries = self.GetEntriesByType(DOMString::from("navigation")); DomRoot::from_ref(&*self.timing)
if !entries.is_empty() {
return DomRoot::from_ref(
entries[0]
.downcast::<PerformanceNavigationTiming>()
.unwrap(),
);
}
unreachable!("Are we trying to expose Performance.timing in workers?");
} }
// https://w3c.github.io/navigation-timing/#dom-performance-navigation // https://w3c.github.io/navigation-timing/#dom-performance-navigation
fn Navigation(&self) -> DomRoot<PerformanceNavigation> { fn Navigation(&self) -> DomRoot<PerformanceNavigation> {
PerformanceNavigation::new(&self.global(), CanGc::note()) DomRoot::from_ref(&*self.navigation)
} }
// https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/HighResolutionTime/Overview.html#dom-performance-now // https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/HighResolutionTime/Overview.html#dom-performance-now

View file

@ -0,0 +1,253 @@
/* 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 https://mozilla.org/MPL/2.0/. */
use std::cell::Cell;
use dom_struct::dom_struct;
use crate::dom::bindings::codegen::Bindings::PerformanceTimingBinding::PerformanceTimingMethods;
use crate::dom::bindings::reflector::{Reflector, reflect_dom_object};
use crate::dom::bindings::root::DomRoot;
use crate::dom::globalscope::GlobalScope;
use crate::script_runtime::CanGc;
#[dom_struct]
pub(crate) struct PerformanceTiming {
reflector_: Reflector,
navigation_start: Cell<u64>,
unload_event_start: Cell<u64>,
unload_event_end: Cell<u64>,
redirect_start: Cell<u64>,
redirect_end: Cell<u64>,
fetch_start: Cell<u64>,
domain_lookup_start: Cell<u64>,
domain_lookup_end: Cell<u64>,
connect_start: Cell<u64>,
connect_end: Cell<u64>,
secure_connection_start: Cell<u64>,
request_start: Cell<u64>,
response_start: Cell<u64>,
response_end: Cell<u64>,
dom_loading: Cell<u64>,
dom_interactive: Cell<u64>,
dom_content_loaded_event_start: Cell<u64>,
dom_content_loaded_event_end: Cell<u64>,
dom_complete: Cell<u64>,
load_event_start: Cell<u64>,
load_event_end: Cell<u64>,
}
#[allow(dead_code)]
impl PerformanceTiming {
pub fn new(
global: &GlobalScope,
navigation_start: u64,
can_gc: CanGc,
) -> DomRoot<PerformanceTiming> {
reflect_dom_object(
Box::new(PerformanceTiming {
reflector_: Reflector::new(),
navigation_start: Cell::new(navigation_start),
unload_event_start: Cell::new(0),
unload_event_end: Cell::new(0),
redirect_start: Cell::new(0),
redirect_end: Cell::new(0),
fetch_start: Cell::new(0),
domain_lookup_start: Cell::new(0),
domain_lookup_end: Cell::new(0),
connect_start: Cell::new(0),
connect_end: Cell::new(0),
secure_connection_start: Cell::new(0),
request_start: Cell::new(0),
response_start: Cell::new(0),
response_end: Cell::new(0),
dom_loading: Cell::new(0),
dom_interactive: Cell::new(0),
dom_content_loaded_event_start: Cell::new(0),
dom_content_loaded_event_end: Cell::new(0),
dom_complete: Cell::new(0),
load_event_start: Cell::new(0),
load_event_end: Cell::new(0),
}),
global,
can_gc,
)
}
pub(crate) fn update_unload_event_start(&self, value: u64) {
self.unload_event_start.set(value);
}
pub(crate) fn update_unload_event_end(&self, value: u64) {
self.unload_event_end.set(value);
}
pub(crate) fn update_redirect_start(&self, value: u64) {
self.redirect_start.set(value);
}
pub(crate) fn update_redirect_end(&self, value: u64) {
self.redirect_end.set(value);
}
pub(crate) fn update_fetch_start(&self, value: u64) {
self.fetch_start.set(value);
}
pub(crate) fn update_domain_lookup_start(&self, value: u64) {
self.domain_lookup_start.set(value);
}
pub(crate) fn update_domain_lookup_end(&self, value: u64) {
self.domain_lookup_end.set(value);
}
pub(crate) fn update_connect_start(&self, value: u64) {
self.connect_start.set(value);
}
pub(crate) fn update_connect_end(&self, value: u64) {
self.connect_end.set(value);
}
pub(crate) fn update_secure_connection_start(&self, value: u64) {
self.secure_connection_start.set(value);
}
pub(crate) fn update_request_start(&self, value: u64) {
self.request_start.set(value);
}
pub(crate) fn update_response_start(&self, value: u64) {
self.response_start.set(value);
}
pub(crate) fn update_response_end(&self, value: u64) {
self.response_end.set(value);
}
pub(crate) fn update_dom_loading(&self, value: u64) {
self.dom_loading.set(value);
}
pub(crate) fn update_dom_interactive(&self, value: u64) {
self.dom_interactive.set(value);
}
pub(crate) fn update_dom_content_loaded_event_start(&self, value: u64) {
self.dom_content_loaded_event_start.set(value);
}
pub(crate) fn update_dom_content_loaded_event_end(&self, value: u64) {
self.dom_content_loaded_event_end.set(value);
}
pub(crate) fn update_dom_complete(&self, value: u64) {
self.dom_complete.set(value);
}
pub(crate) fn update_load_event_start(&self, value: u64) {
self.load_event_start.set(value);
}
pub(crate) fn update_load_event_end(&self, value: u64) {
self.load_event_end.set(value);
}
}
impl PerformanceTimingMethods<crate::DomTypeHolder> for PerformanceTiming {
// https://w3c.github.io/navigation-timing/#dom-performancetiming-navigationstart
fn NavigationStart(&self) -> u64 {
self.navigation_start.get()
}
// https://w3c.github.io/navigation-timing/#dom-performancetiming-unloadeventstart
fn UnloadEventStart(&self) -> u64 {
self.unload_event_start.get()
}
// https://w3c.github.io/navigation-timing/#dom-performancetiming-unloadeventend
fn UnloadEventEnd(&self) -> u64 {
self.unload_event_end.get()
}
// https://w3c.github.io/navigation-timing/#dom-performancetiming-redirectstart
fn RedirectStart(&self) -> u64 {
self.redirect_start.get()
}
// https://w3c.github.io/navigation-timing/#dom-performancetiming-redirectend
fn RedirectEnd(&self) -> u64 {
self.redirect_end.get()
}
// https://w3c.github.io/navigation-timing/#dom-performancetiming-fetchstart
fn FetchStart(&self) -> u64 {
self.fetch_start.get()
}
// https://w3c.github.io/navigation-timing/#dom-performancetiming-domainlookupstart
fn DomainLookupStart(&self) -> u64 {
self.domain_lookup_start.get()
}
// https://w3c.github.io/navigation-timing/#dom-performancetiming-domainlookupend
fn DomainLookupEnd(&self) -> u64 {
self.domain_lookup_end.get()
}
// https://w3c.github.io/navigation-timing/#dom-performancetiming-connectstart
fn ConnectStart(&self) -> u64 {
self.connect_start.get()
}
// https://w3c.github.io/navigation-timing/#dom-performancetiming-connectend
fn ConnectEnd(&self) -> u64 {
self.connect_end.get()
}
// https://w3c.github.io/navigation-timing#dom-performancetiming-secureconnectionstart
fn SecureConnectionStart(&self) -> u64 {
self.secure_connection_start.get()
}
// https://w3c.github.io/navigation-timing/#dom-performancetiming-requeststart
fn RequestStart(&self) -> u64 {
self.request_start.get()
}
// https://w3c.github.io/navigation-timing/#dom-performancetiming-responsestart
fn ResponseStart(&self) -> u64 {
self.response_start.get()
}
// https://w3c.github.io/navigation-timing/#dom-performancetiming-responseend
fn ResponseEnd(&self) -> u64 {
self.response_end.get()
}
// https://w3c.github.io/navigation-timing/#dom-performancetiming-domloading
fn DomLoading(&self) -> u64 {
self.dom_loading.get()
}
// https://w3c.github.io/navigation-timing/#dom-performancetiming-dominteractive
fn DomInteractive(&self) -> u64 {
self.dom_interactive.get()
}
// https://w3c.github.io/navigation-timing/#dom-performancetiming-domcontentloadedeventstart
fn DomContentLoadedEventStart(&self) -> u64 {
self.dom_content_loaded_event_start.get()
}
// https://w3c.github.io/navigation-timing/#dom-performancetiming-domcontentloadedeventend
fn DomContentLoadedEventEnd(&self) -> u64 {
self.dom_content_loaded_event_end.get()
}
// https://w3c.github.io/navigation-timing/#dom-performancetiming-domcomplete
fn DomComplete(&self) -> u64 {
self.dom_complete.get()
}
// https://w3c.github.io/navigation-timing/#dom-performancetiming-loadeventstart
fn LoadEventStart(&self) -> u64 {
self.load_event_start.get()
}
// https://w3c.github.io/navigation-timing/#dom-performancetiming-loadeventend
fn LoadEventEnd(&self) -> u64 {
self.load_event_end.get()
}
}

View file

@ -185,7 +185,6 @@ impl WindowProxy {
assert!(!js_proxy.is_null()); assert!(!js_proxy.is_null());
// Create a new browsing context. // Create a new browsing context.
let current = Some(window.upcast::<GlobalScope>().pipeline_id()); let current = Some(window.upcast::<GlobalScope>().pipeline_id());
let window_proxy = Box::new(WindowProxy::new_inherited( let window_proxy = Box::new(WindowProxy::new_inherited(
browsing_context_id, browsing_context_id,

View file

@ -47,7 +47,7 @@ partial interface Performance {
[Exposed=Window] [Exposed=Window]
partial interface Performance { partial interface Performance {
[SameObject] [SameObject]
readonly attribute PerformanceNavigationTiming timing; readonly attribute PerformanceTiming timing;
[SameObject] [SameObject]
readonly attribute PerformanceNavigation navigation; readonly attribute PerformanceNavigation navigation;
}; };

View file

@ -0,0 +1,30 @@
/* 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 https://mozilla.org/MPL/2.0/. */
// https://w3c.github.io/navigation-timing/#the-performancetiming-interface
[Exposed=Window]
interface PerformanceTiming {
readonly attribute unsigned long long navigationStart;
readonly attribute unsigned long long unloadEventStart;
readonly attribute unsigned long long unloadEventEnd;
readonly attribute unsigned long long redirectStart;
readonly attribute unsigned long long redirectEnd;
readonly attribute unsigned long long fetchStart;
readonly attribute unsigned long long domainLookupStart;
readonly attribute unsigned long long domainLookupEnd;
readonly attribute unsigned long long connectStart;
readonly attribute unsigned long long connectEnd;
readonly attribute unsigned long long secureConnectionStart;
readonly attribute unsigned long long requestStart;
readonly attribute unsigned long long responseStart;
readonly attribute unsigned long long responseEnd;
readonly attribute unsigned long long domLoading;
readonly attribute unsigned long long domInteractive;
readonly attribute unsigned long long domContentLoadedEventStart;
readonly attribute unsigned long long domContentLoadedEventEnd;
readonly attribute unsigned long long domComplete;
readonly attribute unsigned long long loadEventStart;
readonly attribute unsigned long long loadEventEnd;
};