diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 68efa07537c..23d4cfa4fce 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -1086,6 +1086,11 @@ impl Document { // https://html.spec.whatwg.org/multipage/#current-document-readiness 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 { DocumentReadyState::Loading => { if self.window().is_top_level() { @@ -1095,6 +1100,11 @@ impl Document { )); 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 => { if self.window().is_top_level() { @@ -1104,8 +1114,8 @@ impl Document { )); } 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); @@ -2987,6 +2997,10 @@ impl Document { if document.browsing_context().is_none() { return; } + + let performance = window.Performance(); + let timing = performance.Timing(); + let event = Event::new( window.upcast(), atom!("load"), @@ -2996,12 +3010,16 @@ impl Document { ); 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 update_with_current_instant(&document.load_event_start); debug!("About to dispatch load for {:?}", document.url()); 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 update_with_current_instant(&document.load_event_end); @@ -3243,20 +3261,25 @@ impl Document { "Complete before DOMContentLoaded?" ); - update_with_current_instant(&self.dom_content_loaded_event_start); - // Step 4.1. let document = Trusted::new(self); self.owner_global() .task_manager() .dom_manipulation_task_source() - .queue( - task!(fire_dom_content_loaded_event: move || { - let document = document.root(); - document.upcast::().fire_bubbling_event(atom!("DOMContentLoaded"), CanGc::note()); - update_with_current_instant(&document.dom_content_loaded_event_end); - }) - ); + .queue(task!(fire_dom_content_loaded_event: move || { + let document = document.root(); + + let window = document.window(); + 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::().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 self.interactive_time diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index 5d7f1966207..243b6794f8c 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -484,6 +484,7 @@ pub(crate) mod performanceobserver; pub(crate) mod performanceobserverentrylist; pub(crate) mod performancepainttiming; pub(crate) mod performanceresourcetiming; +pub(crate) mod performancetiming; pub(crate) mod permissions; pub(crate) mod permissionstatus; pub(crate) mod plugin; diff --git a/components/script/dom/performance.rs b/components/script/dom/performance.rs index 7d4021ea801..322213b7196 100644 --- a/components/script/dom/performance.rs +++ b/components/script/dom/performance.rs @@ -19,7 +19,7 @@ use crate::dom::bindings::error::{Error, Fallible}; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::num::Finite; 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::eventtarget::EventTarget; use crate::dom::globalscope::GlobalScope; @@ -27,8 +27,8 @@ use crate::dom::performanceentry::PerformanceEntry; use crate::dom::performancemark::PerformanceMark; use crate::dom::performancemeasure::PerformanceMeasure; use crate::dom::performancenavigation::PerformanceNavigation; -use crate::dom::performancenavigationtiming::PerformanceNavigationTiming; use crate::dom::performanceobserver::PerformanceObserver as DOMPerformanceObserver; +use crate::dom::performancetiming::PerformanceTiming; use crate::dom::window::Window; use crate::script_runtime::CanGc; @@ -148,10 +148,23 @@ pub(crate) struct Performance { resource_timing_buffer_current_size: Cell, resource_timing_buffer_pending_full_event: Cell, resource_timing_secondary_entries: DomRefCell>>, + timing: Dom, + navigation: Dom, } 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 { eventtarget: EventTarget::new_inherited(), buffer: DomRefCell::new(PerformanceEntryList::new(Vec::new())), @@ -162,6 +175,8 @@ impl Performance { resource_timing_buffer_current_size: Cell::new(0), resource_timing_buffer_pending_full_event: Cell::new(false), 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, ) -> DomRoot { reflect_dom_object( - Box::new(Performance::new_inherited(navigation_start)), + Box::new(Performance::new_inherited(global, navigation_start, can_gc)), global, can_gc, ) @@ -433,21 +448,13 @@ impl Performance { impl PerformanceMethods for Performance { // 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 - fn Timing(&self) -> DomRoot { - let entries = self.GetEntriesByType(DOMString::from("navigation")); - if !entries.is_empty() { - return DomRoot::from_ref( - entries[0] - .downcast::() - .unwrap(), - ); - } - unreachable!("Are we trying to expose Performance.timing in workers?"); + fn Timing(&self) -> DomRoot { + DomRoot::from_ref(&*self.timing) } // https://w3c.github.io/navigation-timing/#dom-performance-navigation fn Navigation(&self) -> DomRoot { - 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 diff --git a/components/script/dom/performancetiming.rs b/components/script/dom/performancetiming.rs new file mode 100644 index 00000000000..4ef80ebde7d --- /dev/null +++ b/components/script/dom/performancetiming.rs @@ -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, + unload_event_start: Cell, + unload_event_end: Cell, + redirect_start: Cell, + redirect_end: Cell, + fetch_start: Cell, + domain_lookup_start: Cell, + domain_lookup_end: Cell, + connect_start: Cell, + connect_end: Cell, + secure_connection_start: Cell, + request_start: Cell, + response_start: Cell, + response_end: Cell, + dom_loading: Cell, + dom_interactive: Cell, + dom_content_loaded_event_start: Cell, + dom_content_loaded_event_end: Cell, + dom_complete: Cell, + load_event_start: Cell, + load_event_end: Cell, +} + +#[allow(dead_code)] +impl PerformanceTiming { + pub fn new( + global: &GlobalScope, + navigation_start: u64, + can_gc: CanGc, + ) -> DomRoot { + 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 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() + } +} diff --git a/components/script/dom/windowproxy.rs b/components/script/dom/windowproxy.rs index a8decee24ed..9c047b238c1 100644 --- a/components/script/dom/windowproxy.rs +++ b/components/script/dom/windowproxy.rs @@ -185,7 +185,6 @@ impl WindowProxy { assert!(!js_proxy.is_null()); // Create a new browsing context. - let current = Some(window.upcast::().pipeline_id()); let window_proxy = Box::new(WindowProxy::new_inherited( browsing_context_id, diff --git a/components/script_bindings/webidls/Performance.webidl b/components/script_bindings/webidls/Performance.webidl index 46f751cb4ab..98574ac0078 100644 --- a/components/script_bindings/webidls/Performance.webidl +++ b/components/script_bindings/webidls/Performance.webidl @@ -47,7 +47,7 @@ partial interface Performance { [Exposed=Window] partial interface Performance { [SameObject] - readonly attribute PerformanceNavigationTiming timing; + readonly attribute PerformanceTiming timing; [SameObject] readonly attribute PerformanceNavigation navigation; }; diff --git a/components/script_bindings/webidls/PerformanceTiming.webidl b/components/script_bindings/webidls/PerformanceTiming.webidl new file mode 100644 index 00000000000..bc9fb0134b1 --- /dev/null +++ b/components/script_bindings/webidls/PerformanceTiming.webidl @@ -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; +};