diff options
author | ddh <dianehosfelt@gmail.com> | 2018-03-12 22:24:41 +0000 |
---|---|---|
committer | ddh <dianehosfelt@gmail.com> | 2018-11-20 16:21:32 +0000 |
commit | 26007fddd3f8aabfe026f06de64207d31edf5318 (patch) | |
tree | f1a7ef3496871d67907734dc10c2cc9df31f27cf /components/script | |
parent | 3fe83f1d06a50969b2fa731a050b35abdc5520d7 (diff) | |
download | servo-26007fddd3f8aabfe026f06de64207d31edf5318.tar.gz servo-26007fddd3f8aabfe026f06de64207d31edf5318.zip |
refactored performance timing to align with updated spec
refactoring with ResourceFetchMetadata
implemented deprecated window.timing functionality
created ResourceTimingListener trait
fixed w3c links in navigation timing
updated include.ini to run resource timing tests on ci
Diffstat (limited to 'components/script')
25 files changed, 863 insertions, 128 deletions
diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index 62a2727cade..1b430cb12ff 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -80,7 +80,7 @@ use net_traits::request::{Request, RequestInit}; use net_traits::response::HttpsState; use net_traits::response::{Response, ResponseBody}; use net_traits::storage_thread::StorageType; -use net_traits::{Metadata, NetworkError, ReferrerPolicy, ResourceThreads}; +use net_traits::{Metadata, NetworkError, ReferrerPolicy, ResourceFetchTiming, ResourceThreads}; use offscreen_gl_context::GLLimits; use profile_traits::mem::ProfilerChan as MemProfilerChan; use profile_traits::time::ProfilerChan as TimeProfilerChan; @@ -466,6 +466,7 @@ unsafe_no_jsmanaged_fields!(AnalysisEngine, DistanceModel, PanningModel, ParamTy unsafe_no_jsmanaged_fields!(dyn Player<Error = ServoMediaError>); unsafe_no_jsmanaged_fields!(Mutex<MediaFrameRenderer>); unsafe_no_jsmanaged_fields!(RenderApiSender); +unsafe_no_jsmanaged_fields!(ResourceFetchTiming); unsafe impl<'a> JSTraceable for &'a str { #[inline] diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 367ce9e2377..e15d735527b 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -356,6 +356,8 @@ pub struct Document { top_level_dom_complete: Cell<u64>, load_event_start: Cell<u64>, load_event_end: Cell<u64>, + unload_event_start: Cell<u64>, + unload_event_end: Cell<u64>, /// <https://html.spec.whatwg.org/multipage/#concept-document-https-state> https_state: Cell<HttpsState>, /// The document's origin. @@ -406,6 +408,8 @@ pub struct Document { fired_unload: Cell<bool>, /// List of responsive images responsive_images: DomRefCell<Vec<Dom<HTMLImageElement>>>, + /// Number of redirects for the document load + redirect_count: Cell<u16>, } #[derive(JSTraceable, MallocSizeOf)] @@ -2290,6 +2294,14 @@ impl Document { self.load_event_end.get() } + pub fn get_unload_event_start(&self) -> u64 { + self.unload_event_start.get() + } + + pub fn get_unload_event_end(&self) -> u64 { + self.unload_event_end.get() + } + pub fn start_tti(&self) { if self.get_interactive_metrics().needs_tti() { self.tti_window.borrow_mut().start_window(); @@ -2654,6 +2666,8 @@ impl Document { top_level_dom_complete: Cell::new(Default::default()), load_event_start: Cell::new(Default::default()), load_event_end: Cell::new(Default::default()), + unload_event_start: Cell::new(Default::default()), + unload_event_end: Cell::new(Default::default()), https_state: Cell::new(HttpsState::None), origin: origin, referrer: referrer, @@ -2674,6 +2688,7 @@ impl Document { salvageable: Cell::new(true), fired_unload: Cell::new(false), responsive_images: Default::default(), + redirect_count: Cell::new(0), } } @@ -2739,6 +2754,14 @@ impl Document { document } + pub fn get_redirect_count(&self) -> u16 { + self.redirect_count.get() + } + + pub fn set_redirect_count(&self, count: u16) { + self.redirect_count.set(count) + } + fn create_node_list<F: Fn(&Node) -> bool>(&self, callback: F) -> DomRoot<NodeList> { let doc = self.GetDocumentElement(); let maybe_node = doc.r().map(Castable::upcast::<Node>); @@ -4268,6 +4291,9 @@ impl DocumentMethods for Document { return Ok(DomRoot::from_ref(self)); } + // TODO: prompt to unload. + // TODO: set unload_event_start and unload_event_end + window_from_node(self).set_navigation_start(); // Step 7 diff --git a/components/script/dom/eventsource.rs b/components/script/dom/eventsource.rs index 4484d217752..10c912fb8e4 100644 --- a/components/script/dom/eventsource.rs +++ b/components/script/dom/eventsource.rs @@ -16,8 +16,9 @@ use crate::dom::event::Event; use crate::dom::eventtarget::EventTarget; use crate::dom::globalscope::GlobalScope; use crate::dom::messageevent::MessageEvent; +use crate::dom::performanceresourcetiming::InitiatorType; use crate::fetch::FetchCanceller; -use crate::network_listener::{NetworkListener, PreInvoke}; +use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener}; use crate::task_source::{TaskSource, TaskSourceName}; use crate::timers::OneshotTimerCallback; use dom_struct::dom_struct; @@ -34,6 +35,7 @@ use net_traits::request::{CacheMode, CorsSettings, CredentialsMode}; use net_traits::request::{RequestInit, RequestMode}; use net_traits::{CoreResourceMsg, FetchChannels, FetchMetadata}; use net_traits::{FetchResponseListener, FetchResponseMsg, NetworkError}; +use net_traits::{ResourceFetchTiming, ResourceTimingType}; use servo_atoms::Atom; use servo_url::ServoUrl; use std::cell::Cell; @@ -91,6 +93,8 @@ struct EventSourceContext { event_type: String, data: String, last_event_id: String, + + resource_timing: ResourceFetchTiming, } impl EventSourceContext { @@ -398,12 +402,34 @@ impl FetchResponseListener for EventSourceContext { } } - fn process_response_eof(&mut self, _response: Result<(), NetworkError>) { + fn process_response_eof(&mut self, _response: Result<ResourceFetchTiming, NetworkError>) { if let Some(_) = self.incomplete_utf8.take() { self.parse("\u{FFFD}".chars()); } self.reestablish_the_connection(); } + + fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming { + &mut self.resource_timing + } + + fn resource_timing(&self) -> &ResourceFetchTiming { + &self.resource_timing + } + + fn submit_resource_timing(&mut self) { + network_listener::submit_timing(self) + } +} + +impl ResourceTimingListener for EventSourceContext { + fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) { + (InitiatorType::Other, self.event_source.root().url().clone()) + } + + fn resource_timing_global(&self) -> DomRoot<GlobalScope> { + self.event_source.root().global() + } } impl PreInvoke for EventSourceContext { @@ -463,6 +489,10 @@ impl EventSource { self.request.borrow().clone().unwrap() } + pub fn url(&self) -> &ServoUrl { + &self.url + } + // https://html.spec.whatwg.org/multipage/#dom-eventsource pub fn Constructor( global: &GlobalScope, @@ -533,6 +563,7 @@ impl EventSource { event_type: String::new(), data: String::new(), last_event_id: String::new(), + resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource), }; let listener = NetworkListener { context: Arc::new(Mutex::new(context)), diff --git a/components/script/dom/htmlimageelement.rs b/components/script/dom/htmlimageelement.rs index 287688193c0..b07166fde81 100644 --- a/components/script/dom/htmlimageelement.rs +++ b/components/script/dom/htmlimageelement.rs @@ -25,6 +25,7 @@ use crate::dom::element::{reflect_cross_origin_attribute, set_cross_origin_attri use crate::dom::element::{AttributeMutation, Element, RawLayoutElementHelpers}; use crate::dom::event::{Event, EventBubbles, EventCancelable}; use crate::dom::eventtarget::EventTarget; +use crate::dom::globalscope::GlobalScope; use crate::dom::htmlareaelement::HTMLAreaElement; use crate::dom::htmlelement::HTMLElement; use crate::dom::htmlformelement::{FormControl, HTMLFormElement}; @@ -33,15 +34,17 @@ use crate::dom::htmlpictureelement::HTMLPictureElement; use crate::dom::htmlsourceelement::HTMLSourceElement; use crate::dom::mouseevent::MouseEvent; use crate::dom::node::{document_from_node, window_from_node, Node, NodeDamage, UnbindContext}; +use crate::dom::performanceresourcetiming::InitiatorType; use crate::dom::progressevent::ProgressEvent; use crate::dom::values::UNSIGNED_LONG_MAX; use crate::dom::virtualmethods::VirtualMethods; use crate::dom::window::Window; use crate::microtask::{Microtask, MicrotaskRunnable}; -use crate::network_listener::{NetworkListener, PreInvoke}; +use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener}; use crate::script_thread::ScriptThread; use crate::task_source::TaskSource; use cssparser::{Parser, ParserInput}; + use dom_struct::dom_struct; use euclid::Point2D; use html5ever::{LocalName, Prefix}; @@ -54,6 +57,7 @@ use net_traits::image_cache::{CanRequestImages, ImageCache, ImageOrMetadataAvail use net_traits::image_cache::{ImageResponder, ImageResponse, ImageState, PendingImageId}; use net_traits::request::RequestInit; use net_traits::{FetchMetadata, FetchResponseListener, FetchResponseMsg, NetworkError}; +use net_traits::{ResourceFetchTiming, ResourceTimingType}; use num_traits::ToPrimitive; use servo_url::origin::MutableOrigin; use servo_url::ServoUrl; @@ -164,6 +168,11 @@ struct ImageContext { id: PendingImageId, /// Used to mark abort aborted: Cell<bool>, + /// The document associated with this request + doc: Trusted<Document>, + /// timing data for this resource + resource_timing: ResourceFetchTiming, + url: ServoUrl, } impl FetchResponseListener for ImageContext { @@ -213,10 +222,35 @@ impl FetchResponseListener for ImageContext { } } - fn process_response_eof(&mut self, response: Result<(), NetworkError>) { + fn process_response_eof(&mut self, response: Result<ResourceFetchTiming, NetworkError>) { self.image_cache .notify_pending_response(self.id, FetchResponseMsg::ProcessResponseEOF(response)); } + + fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming { + &mut self.resource_timing + } + + fn resource_timing(&self) -> &ResourceFetchTiming { + &self.resource_timing + } + + fn submit_resource_timing(&mut self) { + network_listener::submit_timing(self) + } +} + +impl ResourceTimingListener for ImageContext { + fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) { + ( + InitiatorType::LocalName("img".to_string()), + self.url.clone(), + ) + } + + fn resource_timing_global(&self) -> DomRoot<GlobalScope> { + self.doc.root().global() + } } impl PreInvoke for ImageContext { @@ -306,6 +340,9 @@ impl HTMLImageElement { status: Ok(()), id: id, aborted: Cell::new(false), + doc: Trusted::new(&document), + resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource), + url: img_url.clone(), })); let (action_sender, action_receiver) = ipc::channel().unwrap(); diff --git a/components/script/dom/htmlmediaelement.rs b/components/script/dom/htmlmediaelement.rs index 8092b4a90eb..b5cc9d9b1fd 100644 --- a/components/script/dom/htmlmediaelement.rs +++ b/components/script/dom/htmlmediaelement.rs @@ -25,16 +25,18 @@ use crate::dom::blob::Blob; use crate::dom::document::Document; use crate::dom::element::{AttributeMutation, Element}; use crate::dom::eventtarget::EventTarget; +use crate::dom::globalscope::GlobalScope; use crate::dom::htmlelement::HTMLElement; use crate::dom::htmlsourceelement::HTMLSourceElement; use crate::dom::htmlvideoelement::HTMLVideoElement; use crate::dom::mediaerror::MediaError; use crate::dom::node::{document_from_node, window_from_node, Node, NodeDamage, UnbindContext}; +use crate::dom::performanceresourcetiming::InitiatorType; use crate::dom::promise::Promise; use crate::dom::virtualmethods::VirtualMethods; use crate::fetch::FetchCanceller; use crate::microtask::{Microtask, MicrotaskRunnable}; -use crate::network_listener::{NetworkListener, PreInvoke}; +use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener}; use crate::script_thread::ScriptThread; use crate::task_source::TaskSource; use dom_struct::dom_struct; @@ -46,8 +48,8 @@ use ipc_channel::ipc; use ipc_channel::router::ROUTER; use mime::{self, Mime}; use net_traits::request::{CredentialsMode, Destination, RequestInit}; -use net_traits::NetworkError; use net_traits::{CoreResourceMsg, FetchChannels, FetchMetadata, FetchResponseListener, Metadata}; +use net_traits::{NetworkError, ResourceFetchTiming, ResourceTimingType}; use script_layout_interface::HTMLMediaData; use servo_media::player::frame::{Frame, FrameRenderer}; use servo_media::player::{PlaybackState, Player, PlayerEvent, StreamType}; @@ -706,7 +708,10 @@ impl HTMLMediaElement { ..RequestInit::default() }; - let context = Arc::new(Mutex::new(HTMLMediaElementContext::new(self))); + let context = Arc::new(Mutex::new(HTMLMediaElementContext::new( + self, + self.resource_url.borrow().as_ref().unwrap().clone(), + ))); let (action_sender, action_receiver) = ipc::channel().unwrap(); let window = window_from_node(self); let (task_source, canceller) = window @@ -1459,6 +1464,10 @@ struct HTMLMediaElementContext { next_progress_event: Timespec, /// True if this response is invalid and should be ignored. ignore_response: bool, + /// timing data for this resource + resource_timing: ResourceFetchTiming, + /// url for the resource + url: ServoUrl, } // https://html.spec.whatwg.org/multipage/#media-data-processing-steps-list @@ -1527,7 +1536,7 @@ impl FetchResponseListener for HTMLMediaElementContext { } // https://html.spec.whatwg.org/multipage/#media-data-processing-steps-list - fn process_response_eof(&mut self, status: Result<(), NetworkError>) { + fn process_response_eof(&mut self, status: Result<ResourceFetchTiming, NetworkError>) { if self.ignore_response { // An error was received previously, skip processing the payload. return; @@ -1579,6 +1588,35 @@ impl FetchResponseListener for HTMLMediaElementContext { elem.queue_dedicated_media_source_failure_steps(); } } + + fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming { + &mut self.resource_timing + } + + fn resource_timing(&self) -> &ResourceFetchTiming { + &self.resource_timing + } + + fn submit_resource_timing(&mut self) { + network_listener::submit_timing(self) + } +} + +impl ResourceTimingListener for HTMLMediaElementContext { + fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) { + let initiator_type = InitiatorType::LocalName( + self.elem + .root() + .upcast::<Element>() + .local_name() + .to_string(), + ); + (initiator_type, self.url.clone()) + } + + fn resource_timing_global(&self) -> DomRoot<GlobalScope> { + (document_from_node(&*self.elem.root()).global()) + } } impl PreInvoke for HTMLMediaElementContext { @@ -1589,13 +1627,15 @@ impl PreInvoke for HTMLMediaElementContext { } impl HTMLMediaElementContext { - fn new(elem: &HTMLMediaElement) -> HTMLMediaElementContext { + fn new(elem: &HTMLMediaElement, url: ServoUrl) -> HTMLMediaElementContext { HTMLMediaElementContext { elem: Trusted::new(elem), metadata: None, generation_id: elem.generation_id.get(), next_progress_event: time::get_time() + Duration::milliseconds(350), ignore_response: false, + resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource), + url: url, } } } diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs index bc70965b1ab..a76f506f352 100644 --- a/components/script/dom/htmlscriptelement.rs +++ b/components/script/dom/htmlscriptelement.rs @@ -23,8 +23,9 @@ use crate::dom::globalscope::GlobalScope; use crate::dom::htmlelement::HTMLElement; use crate::dom::node::{document_from_node, window_from_node}; use crate::dom::node::{ChildrenMutation, CloneChildrenFlag, Node}; +use crate::dom::performanceresourcetiming::InitiatorType; use crate::dom::virtualmethods::VirtualMethods; -use crate::network_listener::{NetworkListener, PreInvoke}; +use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener}; use dom_struct::dom_struct; use encoding_rs::Encoding; use html5ever::{LocalName, Prefix}; @@ -33,6 +34,7 @@ use ipc_channel::router::ROUTER; use js::jsval::UndefinedValue; use net_traits::request::{CorsSettings, CredentialsMode, Destination, RequestInit, RequestMode}; use net_traits::{FetchMetadata, FetchResponseListener, Metadata, NetworkError}; +use net_traits::{ResourceFetchTiming, ResourceTimingType}; use servo_atoms::Atom; use servo_config::opts; use servo_url::ServoUrl; @@ -166,6 +168,8 @@ struct ScriptContext { url: ServoUrl, /// Indicates whether the request failed, and why status: Result<(), NetworkError>, + /// Timing object for this resource + resource_timing: ResourceFetchTiming, } impl FetchResponseListener for ScriptContext { @@ -208,7 +212,7 @@ impl FetchResponseListener for ScriptContext { /// <https://html.spec.whatwg.org/multipage/#fetch-a-classic-script> /// step 4-9 - fn process_response_eof(&mut self, response: Result<(), NetworkError>) { + fn process_response_eof(&mut self, response: Result<ResourceFetchTiming, NetworkError>) { // Step 5. let load = response.and(self.status.clone()).map(|_| { let metadata = self.metadata.take().unwrap(); @@ -241,6 +245,35 @@ impl FetchResponseListener for ScriptContext { document.finish_load(LoadType::Script(self.url.clone())); } + + fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming { + &mut self.resource_timing + } + + fn resource_timing(&self) -> &ResourceFetchTiming { + &self.resource_timing + } + + fn submit_resource_timing(&mut self) { + network_listener::submit_timing(self) + } +} + +impl ResourceTimingListener for ScriptContext { + fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) { + let initiator_type = InitiatorType::LocalName( + self.elem + .root() + .upcast::<Element>() + .local_name() + .to_string(), + ); + (initiator_type, self.url.clone()) + } + + fn resource_timing_global(&self) -> DomRoot<GlobalScope> { + (document_from_node(&*self.elem.root()).global()) + } } impl PreInvoke for ScriptContext {} @@ -290,6 +323,7 @@ fn fetch_a_classic_script( metadata: None, url: url.clone(), status: Ok(()), + resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource), })); let (action_sender, action_receiver) = ipc::channel().unwrap(); diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index c03a5a5206f..b9a9bc60663 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -415,10 +415,11 @@ pub mod performance; pub mod performanceentry; pub mod performancemark; pub mod performancemeasure; +pub mod performancenavigationtiming; pub mod performanceobserver; pub mod performanceobserverentrylist; pub mod performancepainttiming; -pub mod performancetiming; +pub mod performanceresourcetiming; pub mod permissions; pub mod permissionstatus; pub mod plugin; diff --git a/components/script/dom/performance.rs b/components/script/dom/performance.rs index f5d041d2903..857dbe569b6 100644 --- a/components/script/dom/performance.rs +++ b/components/script/dom/performance.rs @@ -11,15 +11,16 @@ use crate::dom::bindings::codegen::Bindings::PerformanceBinding::{ use crate::dom::bindings::error::{Error, Fallible}; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::num::Finite; -use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector}; -use crate::dom::bindings::root::{Dom, DomRoot}; +use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; +use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::str::DOMString; +use crate::dom::eventtarget::EventTarget; use crate::dom::globalscope::GlobalScope; use crate::dom::performanceentry::PerformanceEntry; use crate::dom::performancemark::PerformanceMark; use crate::dom::performancemeasure::PerformanceMeasure; +use crate::dom::performancenavigationtiming::PerformanceNavigationTiming; use crate::dom::performanceobserver::PerformanceObserver as DOMPerformanceObserver; -use crate::dom::performancetiming::PerformanceTiming; use crate::dom::window::Window; use dom_struct::dom_struct; use metrics::ToMs; @@ -131,8 +132,7 @@ struct PerformanceObserver { #[dom_struct] pub struct Performance { - reflector_: Reflector, - timing: Option<Dom<PerformanceTiming>>, + eventtarget: EventTarget, entries: DomRefCell<PerformanceEntryList>, observers: DomRefCell<Vec<PerformanceObserver>>, pending_notification_observers_task: Cell<bool>, @@ -140,22 +140,9 @@ pub struct Performance { } impl Performance { - fn new_inherited( - global: &GlobalScope, - navigation_start: u64, - navigation_start_precise: u64, - ) -> Performance { + fn new_inherited(navigation_start_precise: u64) -> Performance { Performance { - reflector_: Reflector::new(), - timing: if global.is::<Window>() { - Some(Dom::from_ref(&*PerformanceTiming::new( - global.as_window(), - navigation_start, - navigation_start_precise, - ))) - } else { - None - }, + eventtarget: EventTarget::new_inherited(), entries: DomRefCell::new(PerformanceEntryList::new(Vec::new())), observers: DomRefCell::new(Vec::new()), pending_notification_observers_task: Cell::new(false), @@ -163,17 +150,9 @@ impl Performance { } } - pub fn new( - global: &GlobalScope, - navigation_start: u64, - navigation_start_precise: u64, - ) -> DomRoot<Performance> { + pub fn new(global: &GlobalScope, navigation_start_precise: u64) -> DomRoot<Performance> { reflect_dom_object( - Box::new(Performance::new_inherited( - global, - navigation_start, - navigation_start_precise, - )), + Box::new(Performance::new_inherited(navigation_start_precise)), global, PerformanceBinding::Wrap, ) @@ -296,21 +275,23 @@ impl Performance { } fn now(&self) -> f64 { - let nav_start = match self.timing { - Some(ref timing) => timing.navigation_start_precise(), - None => self.navigation_start_precise, - }; - (time::precise_time_ns() - nav_start).to_ms() + (time::precise_time_ns() - self.navigation_start_precise).to_ms() } } 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<PerformanceTiming> { - match self.timing { - Some(ref timing) => DomRoot::from_ref(&*timing), - None => unreachable!("Are we trying to expose Performance.timing in workers?"), + fn Timing(&self) -> DomRoot<PerformanceNavigationTiming> { + let entries = self.GetEntriesByType(DOMString::from("navigation")); + if entries.len() > 0 { + return DomRoot::from_ref( + entries[0] + .downcast::<PerformanceNavigationTiming>() + .unwrap(), + ); } + unreachable!("Are we trying to expose Performance.timing in workers?"); } // https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/HighResolutionTime/Overview.html#dom-performance-now @@ -318,6 +299,11 @@ impl PerformanceMethods for Performance { Finite::wrap(self.now()) } + // https://www.w3.org/TR/hr-time-2/#dom-performance-timeorigin + fn TimeOrigin(&self) -> DOMHighResTimeStamp { + Finite::wrap(self.navigation_start_precise as f64) + } + // https://www.w3.org/TR/performance-timeline-2/#dom-performance-getentries fn GetEntries(&self) -> Vec<DomRoot<PerformanceEntry>> { self.entries diff --git a/components/script/dom/performanceentry.rs b/components/script/dom/performanceentry.rs index 7317532f226..334d8b6de85 100644 --- a/components/script/dom/performanceentry.rs +++ b/components/script/dom/performanceentry.rs @@ -59,6 +59,10 @@ impl PerformanceEntry { pub fn start_time(&self) -> f64 { self.start_time } + + pub fn duration(&self) -> f64 { + self.duration + } } impl PerformanceEntryMethods for PerformanceEntry { diff --git a/components/script/dom/performancenavigationtiming.rs b/components/script/dom/performancenavigationtiming.rs new file mode 100644 index 00000000000..8e9ef66c938 --- /dev/null +++ b/components/script/dom/performancenavigationtiming.rs @@ -0,0 +1,126 @@ +/* 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 http://mozilla.org/MPL/2.0/. */ + +use crate::dom::bindings::codegen::Bindings::PerformanceBinding::DOMHighResTimeStamp; +use crate::dom::bindings::codegen::Bindings::PerformanceNavigationTimingBinding::PerformanceNavigationTimingMethods; +use crate::dom::bindings::codegen::Bindings::PerformanceNavigationTimingBinding::{ + self, NavigationType, +}; +use crate::dom::bindings::num::Finite; +use crate::dom::bindings::reflector::reflect_dom_object; +use crate::dom::bindings::root::{Dom, DomRoot}; +use crate::dom::document::Document; +use crate::dom::globalscope::GlobalScope; +use crate::dom::performanceresourcetiming::{InitiatorType, PerformanceResourceTiming}; +use dom_struct::dom_struct; + +#[dom_struct] +// https://w3c.github.io/navigation-timing/#dom-performancenavigationtiming +/// Only the current document resource is included in the performance timeline; +/// there is only one PerformanceNavigationTiming object in the performance timeline. +pub struct PerformanceNavigationTiming { + // https://w3c.github.io/navigation-timing/#PerformanceResourceTiming + performanceresourcetiming: PerformanceResourceTiming, + navigation_start: u64, + navigation_start_precise: u64, + document: Dom<Document>, + nav_type: NavigationType, +} + +impl PerformanceNavigationTiming { + fn new_inherited( + nav_start: u64, + nav_start_precise: u64, + document: &Document, + ) -> PerformanceNavigationTiming { + PerformanceNavigationTiming { + performanceresourcetiming: PerformanceResourceTiming::new_inherited( + document.url(), + InitiatorType::Navigation, + None, + nav_start_precise as f64, + ), + navigation_start: nav_start, + navigation_start_precise: nav_start_precise, + document: Dom::from_ref(document), + nav_type: NavigationType::Navigate, + } + } + + pub fn new( + global: &GlobalScope, + nav_start: u64, + nav_start_precise: u64, + document: &Document, + ) -> DomRoot<PerformanceNavigationTiming> { + reflect_dom_object( + Box::new(PerformanceNavigationTiming::new_inherited( + nav_start, + nav_start_precise, + document, + )), + global, + PerformanceNavigationTimingBinding::Wrap, + ) + } +} + +// https://w3c.github.io/navigation-timing/ +impl PerformanceNavigationTimingMethods for PerformanceNavigationTiming { + // https://w3c.github.io/navigation-timing/#dom-performancenavigationtiming-unloadeventstart + fn UnloadEventStart(&self) -> DOMHighResTimeStamp { + Finite::wrap(self.document.get_unload_event_start() as f64) + } + + // https://w3c.github.io/navigation-timing/#dom-performancenavigationtiming-unloadeventend + fn UnloadEventEnd(&self) -> DOMHighResTimeStamp { + Finite::wrap(self.document.get_unload_event_end() as f64) + } + + // https://w3c.github.io/navigation-timing/#dom-performancenavigationtiming-dominteractive + fn DomInteractive(&self) -> DOMHighResTimeStamp { + Finite::wrap(self.document.get_dom_interactive() as f64) + } + + // https://w3c.github.io/navigation-timing/#dom-performancenavigationtiming-domcontentloadedeventstart + fn DomContentLoadedEventStart(&self) -> DOMHighResTimeStamp { + Finite::wrap(self.document.get_dom_content_loaded_event_start() as f64) + } + + // https://w3c.github.io/navigation-timing/#dom-performancenavigationtiming-domcontentloadedeventstart + fn DomContentLoadedEventEnd(&self) -> DOMHighResTimeStamp { + Finite::wrap(self.document.get_dom_content_loaded_event_end() as f64) + } + + // https://w3c.github.io/navigation-timing/#dom-performancenavigationtiming-domcomplete + fn DomComplete(&self) -> DOMHighResTimeStamp { + Finite::wrap(self.document.get_dom_complete() as f64) + } + + // https://w3c.github.io/navigation-timing/#dom-performancenavigationtiming-loadeventstart + fn LoadEventStart(&self) -> DOMHighResTimeStamp { + Finite::wrap(self.document.get_load_event_start() as f64) + } + + // https://w3c.github.io/navigation-timing/#dom-performancenavigationtiming-loadeventend + fn LoadEventEnd(&self) -> DOMHighResTimeStamp { + Finite::wrap(self.document.get_load_event_end() as f64) + } + + // https://w3c.github.io/navigation-timing/#dom-performancenavigationtiming-type + fn Type(&self) -> NavigationType { + self.nav_type.clone() + } + + // https://w3c.github.io/navigation-timing/#dom-performancenavigationtiming-redirectcount + fn RedirectCount(&self) -> u16 { + self.document.get_redirect_count() + } + + // check-tidy: no specs after this line + // Servo-only timing for when top-level content (not iframes) is complete + fn TopLevelDomComplete(&self) -> DOMHighResTimeStamp { + Finite::wrap(self.document.get_top_level_dom_complete() as f64) + } +} diff --git a/components/script/dom/performanceobserver.rs b/components/script/dom/performanceobserver.rs index cf7ba499f28..dc66e88b5a3 100644 --- a/components/script/dom/performanceobserver.rs +++ b/components/script/dom/performanceobserver.rs @@ -22,9 +22,11 @@ use std::rc::Rc; /// List of allowed performance entry types. const VALID_ENTRY_TYPES: &'static [&'static str] = &[ - "mark", // User Timing API - "measure", // User Timing API - // "resource", XXX Resource Timing API + "mark", // User Timing API + "measure", // User Timing API + "resource", // Resource Timing API + "navigation", // Navigation Timing API + // "frame", //TODO Frame Timing API // "server", XXX Server Timing API "paint", // Paint Timing API ]; diff --git a/components/script/dom/performanceresourcetiming.rs b/components/script/dom/performanceresourcetiming.rs new file mode 100644 index 00000000000..22842160267 --- /dev/null +++ b/components/script/dom/performanceresourcetiming.rs @@ -0,0 +1,187 @@ +/* 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 http://mozilla.org/MPL/2.0/. */ + +use crate::dom::bindings::codegen::Bindings::PerformanceBinding::DOMHighResTimeStamp; +use crate::dom::bindings::codegen::Bindings::PerformanceResourceTimingBinding::{ + self, PerformanceResourceTimingMethods, +}; +use crate::dom::bindings::num::Finite; +use crate::dom::bindings::reflector::reflect_dom_object; +use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::str::DOMString; +use crate::dom::globalscope::GlobalScope; +use crate::dom::performanceentry::PerformanceEntry; +use dom_struct::dom_struct; +use net_traits::ResourceFetchTiming; +use servo_url::ServoUrl; + +// TODO UA may choose to limit how many resources are included as PerformanceResourceTiming objects +// recommended minimum is 150, can be changed by setResourceTimingBufferSize in performance +// https://w3c.github.io/resource-timing/#sec-extensions-performance-interface + +// TODO Cross origin resources MUST BE INCLUDED as PerformanceResourceTiming objects +// https://w3c.github.io/resource-timing/#sec-cross-origin-resources + +// TODO CSS, Beacon +#[derive(Debug, JSTraceable, MallocSizeOf, PartialEq)] +pub enum InitiatorType { + LocalName(String), + Navigation, + XMLHttpRequest, + Fetch, + Other, +} + +#[dom_struct] +pub struct PerformanceResourceTiming { + entry: PerformanceEntry, + initiator_type: InitiatorType, + next_hop: Option<DOMString>, + worker_start: f64, + redirect_start: f64, + redirect_end: f64, + fetch_start: f64, + domain_lookup_start: f64, + domain_lookup_end: f64, + connect_start: f64, + connect_end: f64, + secure_connection_start: f64, + request_start: f64, + response_start: f64, + response_end: f64, + // transfer_size: f64, //size in octets + // encoded_body_size: f64, //size in octets + // decoded_body_size: f64, //size in octets +} + +// TODO(#21254): startTime +// TODO(#21255): duration +// TODO(#21269): next_hop +// TODO(#21264): worker_start +// TODO(#21256): redirect_start +// TODO(#21257): redirect_end +// TODO(#21258): fetch_start +// TODO(#21259): domain_lookup_start +// TODO(#21260): domain_lookup_end +// TODO(#21261): connect_start +// TODO(#21262): connect_end +// TODO(#21263): response_end +impl PerformanceResourceTiming { + pub fn new_inherited( + url: ServoUrl, + initiator_type: InitiatorType, + next_hop: Option<DOMString>, + fetch_start: f64, + ) -> PerformanceResourceTiming { + PerformanceResourceTiming { + entry: PerformanceEntry::new_inherited( + DOMString::from(url.into_string()), + DOMString::from("resource"), + fetch_start, + 0., + ), + initiator_type: initiator_type, + next_hop: next_hop, + worker_start: 0., + redirect_start: 0., + redirect_end: 0., + fetch_start: fetch_start, + domain_lookup_start: 0., + domain_lookup_end: 0., + connect_start: 0., + connect_end: 0., + secure_connection_start: 0., + request_start: 0., + response_start: 0., + response_end: 0., + } + } + + //TODO fetch start should be in RFT + #[allow(unrooted_must_root)] + fn from_resource_timing( + url: ServoUrl, + initiator_type: InitiatorType, + next_hop: Option<DOMString>, + resource_timing: &ResourceFetchTiming, + ) -> PerformanceResourceTiming { + PerformanceResourceTiming { + entry: PerformanceEntry::new_inherited( + DOMString::from(url.into_string()), + DOMString::from("resource"), + 0., + 0., + ), + initiator_type: initiator_type, + next_hop: next_hop, + worker_start: 0., + redirect_start: 0., + redirect_end: 0., + fetch_start: 0., + domain_lookup_start: 0., + domain_lookup_end: 0., + connect_start: 0., + connect_end: 0., + secure_connection_start: 0., + request_start: resource_timing.request_start as f64, + response_start: resource_timing.response_start as f64, + response_end: 0., + } + } + + pub fn new( + global: &GlobalScope, + url: ServoUrl, + initiator_type: InitiatorType, + next_hop: Option<DOMString>, + resource_timing: &ResourceFetchTiming, + ) -> DomRoot<PerformanceResourceTiming> { + reflect_dom_object( + Box::new(PerformanceResourceTiming::from_resource_timing( + url, + initiator_type, + next_hop, + resource_timing, + )), + global, + PerformanceResourceTimingBinding::Wrap, + ) + } +} + +// https://w3c.github.io/resource-timing/ +impl PerformanceResourceTimingMethods for PerformanceResourceTiming { + // https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-initiatortype + fn InitiatorType(&self) -> DOMString { + match self.initiator_type { + InitiatorType::LocalName(ref n) => DOMString::from(n.clone()), + InitiatorType::Navigation => DOMString::from("navigation"), + InitiatorType::XMLHttpRequest => DOMString::from("xmlhttprequest"), + InitiatorType::Fetch => DOMString::from("fetch"), + InitiatorType::Other => DOMString::from("other"), + } + } + + // https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-nexthopprotocol + // returns the ALPN protocol ID of the network protocol used to fetch the resource + // when a proxy is configured + fn NextHopProtocol(&self) -> DOMString { + match self.next_hop { + Some(ref protocol) => DOMString::from(protocol.clone()), + None => DOMString::from(""), + } + } + + // https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-requeststart + fn RequestStart(&self) -> DOMHighResTimeStamp { + // TODO + Finite::wrap(self.request_start) + } + + // https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-responsestart + fn ResponseStart(&self) -> DOMHighResTimeStamp { + // TODO + Finite::wrap(self.response_start) + } +} diff --git a/components/script/dom/servoparser/mod.rs b/components/script/dom/servoparser/mod.rs index a023fb19651..3c7e45f91de 100644 --- a/components/script/dom/servoparser/mod.rs +++ b/components/script/dom/servoparser/mod.rs @@ -13,7 +13,7 @@ use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; use crate::dom::bindings::codegen::Bindings::ServoParserBinding; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::refcounted::Trusted; -use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; +use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector}; use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom, RootedReference}; use crate::dom::bindings::settings_stack::is_execution_stack_empty; use crate::dom::bindings::str::DOMString; @@ -28,6 +28,8 @@ use crate::dom::htmlimageelement::HTMLImageElement; use crate::dom::htmlscriptelement::{HTMLScriptElement, ScriptResult}; use crate::dom::htmltemplateelement::HTMLTemplateElement; use crate::dom::node::Node; +use crate::dom::performanceentry::PerformanceEntry; +use crate::dom::performancenavigationtiming::PerformanceNavigationTiming; use crate::dom::processinginstruction::ProcessingInstruction; use crate::dom::text::Text; use crate::dom::virtualmethods::vtable_for; @@ -43,8 +45,10 @@ use hyper_serde::Serde; use mime::{self, Mime}; use msg::constellation_msg::PipelineId; use net_traits::{FetchMetadata, FetchResponseListener, Metadata, NetworkError}; -use profile_traits::time::{profile, ProfilerCategory, TimerMetadataReflowType}; -use profile_traits::time::{TimerMetadata, TimerMetadataFrameType}; +use net_traits::{ResourceFetchTiming, ResourceTimingType}; +use profile_traits::time::{ + profile, ProfilerCategory, TimerMetadata, TimerMetadataFrameType, TimerMetadataReflowType, +}; use script_traits::DocumentActivity; use servo_config::prefs::PREFS; use servo_url::ServoUrl; @@ -656,6 +660,8 @@ pub struct ParserContext { id: PipelineId, /// The URL for this document. url: ServoUrl, + /// timing data for this resource + resource_timing: ResourceFetchTiming, } impl ParserContext { @@ -665,6 +671,7 @@ impl ParserContext { is_synthesized_document: false, id: id, url: url, + resource_timing: ResourceFetchTiming::new(ResourceTimingType::Navigation), } } } @@ -792,7 +799,10 @@ impl FetchResponseListener for ParserContext { parser.parse_bytes_chunk(payload); } - fn process_response_eof(&mut self, status: Result<(), NetworkError>) { + // This method is called via script_thread::handle_fetch_eof, so we must call + // submit_resource_timing in this function + // Resource listeners are called via net_traits::Action::process, which handles submission for them + fn process_response_eof(&mut self, status: Result<ResourceFetchTiming, NetworkError>) { let parser = match self.parser.as_ref() { Some(parser) => parser.root(), None => return, @@ -801,15 +811,53 @@ impl FetchResponseListener for ParserContext { return; } - if let Err(err) = status { + match status { + // are we throwing this away or can we use it? + Ok(_) => (), // TODO(Savago): we should send a notification to callers #5463. - debug!("Failed to load page URL {}, error: {:?}", self.url, err); + Err(err) => debug!("Failed to load page URL {}, error: {:?}", self.url, err), } + parser + .document + .set_redirect_count(self.resource_timing.redirect_count); + parser.last_chunk_received.set(true); if !parser.suspended.get() { parser.parse_sync(); } + + //TODO only submit if this is the current document resource + self.submit_resource_timing(); + } + + fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming { + &mut self.resource_timing + } + + fn resource_timing(&self) -> &ResourceFetchTiming { + &self.resource_timing + } + + // store a PerformanceNavigationTiming entry in the globalscope's Performance buffer + fn submit_resource_timing(&mut self) { + let parser = match self.parser.as_ref() { + Some(parser) => parser.root(), + None => return, + }; + if parser.aborted.get() { + return; + } + + let document = &parser.document; + + //TODO nav_start and nav_start_precise + let performance_entry = + PerformanceNavigationTiming::new(&document.global(), 0, 0, &document); + document + .global() + .performance() + .queue_entry(performance_entry.upcast::<PerformanceEntry>(), true); } } diff --git a/components/script/dom/webidls/Performance.webidl b/components/script/dom/webidls/Performance.webidl index 85f8fb58b27..52f5c7e1797 100644 --- a/components/script/dom/webidls/Performance.webidl +++ b/components/script/dom/webidls/Performance.webidl @@ -10,14 +10,10 @@ typedef double DOMHighResTimeStamp; typedef sequence<PerformanceEntry> PerformanceEntryList; [Exposed=(Window, Worker)] -interface Performance { +interface Performance : EventTarget { DOMHighResTimeStamp now(); -}; - -[Exposed=(Window)] -partial interface Performance { - readonly attribute PerformanceTiming timing; - /* readonly attribute PerformanceNavigation navigation; */ + readonly attribute DOMHighResTimeStamp timeOrigin; + // [Default] object toJSON(); }; // https://w3c.github.io/performance-timeline/#extensions-to-the-performance-interface @@ -39,3 +35,10 @@ partial interface Performance { void measure(DOMString measureName, optional DOMString startMark, optional DOMString endMark); void clearMeasures(optional DOMString measureName); }; + +// FIXME(avada): this should be deprecated, but is currently included for web compat +// https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview.html#performance-timing-attribute +[Exposed=(Window)] +partial interface Performance { + PerformanceNavigationTiming timing(); +}; diff --git a/components/script/dom/webidls/PerformanceNavigationTiming.webidl b/components/script/dom/webidls/PerformanceNavigationTiming.webidl new file mode 100644 index 00000000000..cde173c67e2 --- /dev/null +++ b/components/script/dom/webidls/PerformanceNavigationTiming.webidl @@ -0,0 +1,32 @@ +/* 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 http://mozilla.org/MPL/2.0/. */ +/* + * The origin of this IDL file is + * https://w3c.github.io/navigation-timing/#dom-performancenavigationtiming + */ + +enum NavigationType { + "navigate", + "reload", + "back_forward", + "prerender" +}; + +[Exposed=Window] +interface PerformanceNavigationTiming : PerformanceResourceTiming { + readonly attribute DOMHighResTimeStamp unloadEventStart; + readonly attribute DOMHighResTimeStamp unloadEventEnd; + readonly attribute DOMHighResTimeStamp domInteractive; + readonly attribute DOMHighResTimeStamp domContentLoadedEventStart; + readonly attribute DOMHighResTimeStamp domContentLoadedEventEnd; + readonly attribute DOMHighResTimeStamp domComplete; + readonly attribute DOMHighResTimeStamp loadEventStart; + readonly attribute DOMHighResTimeStamp loadEventEnd; + readonly attribute NavigationType type; + readonly attribute unsigned short redirectCount; + // [Default] object toJSON(); + /* Servo-only attribute for measuring when the top-level document (not iframes) is complete. */ + [Pref="dom.testperf.enabled"] + readonly attribute DOMHighResTimeStamp topLevelDomComplete; +}; diff --git a/components/script/dom/webidls/PerformanceResourceTiming.webidl b/components/script/dom/webidls/PerformanceResourceTiming.webidl new file mode 100644 index 00000000000..ca8b4c99b81 --- /dev/null +++ b/components/script/dom/webidls/PerformanceResourceTiming.webidl @@ -0,0 +1,35 @@ +/* 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 http://mozilla.org/MPL/2.0/. */ +/* + * The origin of this IDL file is + * https://w3c.github.io/resource-timing/ + */ + +[Exposed=(Window,Worker)] +interface PerformanceResourceTiming : PerformanceEntry { + readonly attribute DOMString initiatorType; + readonly attribute DOMString nextHopProtocol; + // readonly attribute DOMHighResTimeStamp workerStart; + // readonly attribute DOMHighResTimeStamp redirectStart; + // readonly attribute DOMHighResTimeStamp redirectEnd; + // readonly attribute DOMHighResTimeStamp fetchStart; + // readonly attribute DOMHighResTimeStamp domainLookupStart; + // readonly attribute DOMHighResTimeStamp domainLookupEnd; + // readonly attribute DOMHighResTimeStamp connectStart; + // readonly attribute DOMHighResTimeStamp connectEnd; + // readonly attribute DOMHighResTimeStamp secureConnectionStart; + readonly attribute DOMHighResTimeStamp requestStart; + readonly attribute DOMHighResTimeStamp responseStart; + // readonly attribute DOMHighResTimeStamp responseEnd; + /// readonly attribute unsigned long long transferSize; + /// readonly attribute unsigned long long encodedBodySize; + /// readonly attribute unsigned long long decodedBodySize; + // [Default] object toJSON(); +}; + +// partial interface Performance { +// void clearResourceTimings(); +// void setResourceTimingBufferSize(unsigned long maxSize); +// attribute EventHandler onresourcetimingbufferfull; +// }; diff --git a/components/script/dom/webidls/PerformanceTiming.webidl b/components/script/dom/webidls/PerformanceTiming.webidl deleted file mode 100644 index d2fcb1faec5..00000000000 --- a/components/script/dom/webidls/PerformanceTiming.webidl +++ /dev/null @@ -1,35 +0,0 @@ -/* 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/. */ -/* - * The origin of this IDL file is - * https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview.html#sec-navigation-timing-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; - /* Servo-onnly attribute for measuring when the top-level document (not iframes) is complete. */ - [Pref="dom.testperf.enabled"] - readonly attribute unsigned long long topLevelDomComplete; -}; diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 338c78111e5..b07998de6d4 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -812,11 +812,7 @@ impl WindowMethods for Window { fn Performance(&self) -> DomRoot<Performance> { self.performance.or_init(|| { let global_scope = self.upcast::<GlobalScope>(); - Performance::new( - global_scope, - self.navigation_start.get(), - self.navigation_start_precise.get(), - ) + Performance::new(global_scope, self.navigation_start_precise.get()) }) } diff --git a/components/script/dom/workerglobalscope.rs b/components/script/dom/workerglobalscope.rs index fb29bc1c740..99cee52eaa1 100644 --- a/components/script/dom/workerglobalscope.rs +++ b/components/script/dom/workerglobalscope.rs @@ -357,11 +357,7 @@ impl WorkerGlobalScopeMethods for WorkerGlobalScope { fn Performance(&self) -> DomRoot<Performance> { self.performance.or_init(|| { let global_scope = self.upcast::<GlobalScope>(); - Performance::new( - global_scope, - 0, /* navigation start is not used in workers */ - self.navigation_start_precise, - ) + Performance::new(global_scope, self.navigation_start_precise) }) } diff --git a/components/script/dom/xmlhttprequest.rs b/components/script/dom/xmlhttprequest.rs index 3e1644536ab..18628f27f70 100644 --- a/components/script/dom/xmlhttprequest.rs +++ b/components/script/dom/xmlhttprequest.rs @@ -28,6 +28,7 @@ use crate::dom::globalscope::GlobalScope; use crate::dom::headers::is_forbidden_header_name; use crate::dom::htmlformelement::{encode_multipart_form_data, generate_boundary}; use crate::dom::node::Node; +use crate::dom::performanceresourcetiming::InitiatorType; use crate::dom::progressevent::ProgressEvent; use crate::dom::servoparser::ServoParser; use crate::dom::urlsearchparams::URLSearchParams; @@ -36,7 +37,7 @@ use crate::dom::workerglobalscope::WorkerGlobalScope; use crate::dom::xmlhttprequesteventtarget::XMLHttpRequestEventTarget; use crate::dom::xmlhttprequestupload::XMLHttpRequestUpload; use crate::fetch::FetchCanceller; -use crate::network_listener::{NetworkListener, PreInvoke}; +use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener}; use crate::task_source::networking::NetworkingTaskSource; use crate::task_source::TaskSourceName; use crate::timers::{OneshotTimerCallback, OneshotTimerHandle}; @@ -63,6 +64,7 @@ use net_traits::trim_http_whitespace; use net_traits::CoreResourceMsg::Fetch; use net_traits::{FetchChannels, FetchMetadata, FilteredMetadata}; use net_traits::{FetchResponseListener, NetworkError, ReferrerPolicy}; +use net_traits::{ResourceFetchTiming, ResourceTimingType}; use script_traits::DocumentActivity; use servo_atoms::Atom; use servo_url::ServoUrl; @@ -95,6 +97,7 @@ struct XHRContext { gen_id: GenerationId, buf: DomRefCell<Vec<u8>>, sync_status: DomRefCell<Option<ErrorResult>>, + resource_timing: ResourceFetchTiming, } #[derive(Clone)] @@ -257,13 +260,41 @@ impl XMLHttpRequest { .process_data_available(self.gen_id, self.buf.borrow().clone()); } - fn process_response_eof(&mut self, response: Result<(), NetworkError>) { + fn process_response_eof( + &mut self, + response: Result<ResourceFetchTiming, NetworkError>, + ) { let rv = self .xhr .root() - .process_response_complete(self.gen_id, response); + .process_response_complete(self.gen_id, response.map(|_| ())); *self.sync_status.borrow_mut() = Some(rv); } + + fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming { + &mut self.resource_timing + } + + fn resource_timing(&self) -> &ResourceFetchTiming { + &self.resource_timing + } + + fn submit_resource_timing(&mut self) { + network_listener::submit_timing(self) + } + } + + impl ResourceTimingListener for XHRContext { + fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) { + ( + InitiatorType::XMLHttpRequest, + self.resource_timing_global().get_url().clone(), + ) + } + + fn resource_timing_global(&self) -> DomRoot<GlobalScope> { + self.xhr.root().global() + } } impl PreInvoke for XHRContext { @@ -1424,6 +1455,7 @@ impl XMLHttpRequest { gen_id: self.generation_id.get(), buf: DomRefCell::new(vec![]), sync_status: DomRefCell::new(None), + resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource), })); let (task_source, script_port) = if self.sync.get() { diff --git a/components/script/fetch.rs b/components/script/fetch.rs index 49e441740bc..922ee7d9d03 100644 --- a/components/script/fetch.rs +++ b/components/script/fetch.rs @@ -14,11 +14,12 @@ use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::trace::RootedTraceableBox; use crate::dom::globalscope::GlobalScope; use crate::dom::headers::Guard; +use crate::dom::performanceresourcetiming::InitiatorType; use crate::dom::promise::Promise; use crate::dom::request::Request; use crate::dom::response::Response; use crate::dom::serviceworkerglobalscope::ServiceWorkerGlobalScope; -use crate::network_listener::{NetworkListener, PreInvoke}; +use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener}; use crate::task_source::TaskSourceName; use ipc_channel::ipc; use ipc_channel::router::ROUTER; @@ -28,6 +29,7 @@ use net_traits::request::{Request as NetTraitsRequest, ServiceWorkersMode}; use net_traits::CoreResourceMsg::Fetch as NetTraitsFetch; use net_traits::{FetchChannels, FetchResponseListener, NetworkError}; use net_traits::{FetchMetadata, FilteredMetadata, Metadata}; +use net_traits::{ResourceFetchTiming, ResourceTimingType}; use servo_url::ServoUrl; use std::mem; use std::rc::Rc; @@ -37,6 +39,7 @@ struct FetchContext { fetch_promise: Option<TrustedPromise>, response_object: Trusted<Response>, body: Vec<u8>, + resource_timing: ResourceFetchTiming, } /// RAII fetch canceller object. By default initialized to not having a canceller @@ -143,6 +146,8 @@ pub fn Fetch( }, Ok(r) => r.get_request(), }; + let timing_type = request.timing_type(); + let mut request_init = request_init_from_request(request); // Step 3 @@ -159,6 +164,7 @@ pub fn Fetch( fetch_promise: Some(TrustedPromise::new(promise.clone())), response_object: Trusted::new(&*response), body: vec![], + resource_timing: ResourceFetchTiming::new(timing_type), })); let listener = NetworkListener { context: fetch_context, @@ -250,7 +256,7 @@ impl FetchResponseListener for FetchContext { self.body.append(&mut chunk); } - fn process_response_eof(&mut self, _response: Result<(), NetworkError>) { + fn process_response_eof(&mut self, _response: Result<ResourceFetchTiming, NetworkError>) { let response = self.response_object.root(); let global = response.global(); let cx = global.get_cx(); @@ -259,6 +265,35 @@ impl FetchResponseListener for FetchContext { // TODO // ... trailerObject is not supported in Servo yet. } + + fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming { + &mut self.resource_timing + } + + fn resource_timing(&self) -> &ResourceFetchTiming { + &self.resource_timing + } + + fn submit_resource_timing(&mut self) { + // navigation submission is handled in servoparser/mod.rs + match self.resource_timing.timing_type { + ResourceTimingType::Resource => network_listener::submit_timing(self), + _ => {}, + }; + } +} + +impl ResourceTimingListener for FetchContext { + fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) { + ( + InitiatorType::Fetch, + self.resource_timing_global().get_url().clone(), + ) + } + + fn resource_timing_global(&self) -> DomRoot<GlobalScope> { + self.response_object.root().global() + } } fn fill_headers_with_metadata(r: DomRoot<Response>, m: Metadata) { diff --git a/components/script/layout_image.rs b/components/script/layout_image.rs index 59dac86e784..e47218cffda 100644 --- a/components/script/layout_image.rs +++ b/components/script/layout_image.rs @@ -7,20 +7,28 @@ //! no guarantee that the responsible nodes will still exist in the future if the //! layout thread holds on to them during asynchronous operations. +use crate::dom::bindings::refcounted::Trusted; use crate::dom::bindings::reflector::DomObject; +use crate::dom::bindings::root::DomRoot; +use crate::dom::document::Document; +use crate::dom::globalscope::GlobalScope; use crate::dom::node::{document_from_node, Node}; -use crate::network_listener::{NetworkListener, PreInvoke}; +use crate::dom::performanceresourcetiming::InitiatorType; +use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener}; use ipc_channel::ipc; use ipc_channel::router::ROUTER; use net_traits::image_cache::{ImageCache, PendingImageId}; use net_traits::request::{Destination, RequestInit as FetchRequestInit}; use net_traits::{FetchMetadata, FetchResponseListener, FetchResponseMsg, NetworkError}; +use net_traits::{ResourceFetchTiming, ResourceTimingType}; use servo_url::ServoUrl; use std::sync::{Arc, Mutex}; struct LayoutImageContext { id: PendingImageId, cache: Arc<dyn ImageCache>, + resource_timing: ResourceFetchTiming, + doc: Trusted<Document>, } impl FetchResponseListener for LayoutImageContext { @@ -36,10 +44,35 @@ impl FetchResponseListener for LayoutImageContext { .notify_pending_response(self.id, FetchResponseMsg::ProcessResponseChunk(payload)); } - fn process_response_eof(&mut self, response: Result<(), NetworkError>) { + fn process_response_eof(&mut self, response: Result<ResourceFetchTiming, NetworkError>) { self.cache .notify_pending_response(self.id, FetchResponseMsg::ProcessResponseEOF(response)); } + + fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming { + &mut self.resource_timing + } + + fn resource_timing(&self) -> &ResourceFetchTiming { + &self.resource_timing + } + + fn submit_resource_timing(&mut self) { + network_listener::submit_timing(self) + } +} + +impl ResourceTimingListener for LayoutImageContext { + fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) { + ( + InitiatorType::Other, + self.resource_timing_global().get_url().clone(), + ) + } + + fn resource_timing_global(&self) -> DomRoot<GlobalScope> { + self.doc.root().global() + } } impl PreInvoke for LayoutImageContext {} @@ -50,9 +83,13 @@ pub fn fetch_image_for_layout( id: PendingImageId, cache: Arc<dyn ImageCache>, ) { + let document = document_from_node(node); + let context = Arc::new(Mutex::new(LayoutImageContext { id: id, cache: cache, + resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource), + doc: Trusted::new(&document), })); let document = document_from_node(node); diff --git a/components/script/network_listener.rs b/components/script/network_listener.rs index 99042d42c8d..53e9360eee0 100644 --- a/components/script/network_listener.rs +++ b/components/script/network_listener.rs @@ -2,10 +2,16 @@ * 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 crate::dom::bindings::inheritance::Castable; +use crate::dom::bindings::root::DomRoot; +use crate::dom::globalscope::GlobalScope; +use crate::dom::performanceentry::PerformanceEntry; +use crate::dom::performanceresourcetiming::{InitiatorType, PerformanceResourceTiming}; use crate::task::{TaskCanceller, TaskOnce}; use crate::task_source::networking::NetworkingTaskSource; use crate::task_source::TaskSource; -use net_traits::{Action, FetchResponseListener, FetchResponseMsg}; +use net_traits::{Action, FetchResponseListener, FetchResponseMsg, ResourceTimingType}; +use servo_url::ServoUrl; use std::sync::{Arc, Mutex}; /// An off-thread sink for async network event tasks. All such events are forwarded to @@ -16,6 +22,39 @@ pub struct NetworkListener<Listener: PreInvoke + Send + 'static> { pub canceller: Option<TaskCanceller>, } +pub trait ResourceTimingListener { + fn resource_timing_information(&self) -> (InitiatorType, ServoUrl); + fn resource_timing_global(&self) -> DomRoot<GlobalScope>; +} + +pub fn submit_timing<T: ResourceTimingListener + FetchResponseListener>(listener: &T) { + if listener.resource_timing().timing_type != ResourceTimingType::Resource { + warn!( + "Submitting non-resource ({:?}) timing as resource", + listener.resource_timing().timing_type + ); + return; + } + + let (initiator_type, url) = listener.resource_timing_information(); + if initiator_type == InitiatorType::Other { + warn!("Ignoring InitiatorType::Other resource {:?}", url); + return; + } + + let global = listener.resource_timing_global(); + let performance_entry = PerformanceResourceTiming::new( + &global, + url, + initiator_type, + None, + &listener.resource_timing(), + ); + global + .performance() + .queue_entry(performance_entry.upcast::<PerformanceEntry>(), false); +} + impl<Listener: PreInvoke + Send + 'static> NetworkListener<Listener> { pub fn notify<A: Action<Listener> + Send + 'static>(&self, action: A) { let task = ListenerTask { diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 70c541b14c8..21c87d772e9 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -107,7 +107,10 @@ use net_traits::image_cache::{ImageCache, PendingImageResponse}; use net_traits::request::{CredentialsMode, Destination, RedirectMode, RequestInit}; use net_traits::storage_thread::StorageType; use net_traits::{FetchMetadata, FetchResponseListener, FetchResponseMsg}; -use net_traits::{Metadata, NetworkError, ReferrerPolicy, ResourceThreads}; +use net_traits::{ + Metadata, NetworkError, ReferrerPolicy, ResourceFetchTiming, ResourceThreads, + ResourceTimingType, +}; use profile_traits::mem::{self as profile_mem, OpaqueSender, ReportsChan}; use profile_traits::time::{self as profile_time, profile, ProfilerCategory}; use script_layout_interface::message::{self, Msg, NewLayoutThreadInfo, ReflowGoal}; @@ -3097,9 +3100,12 @@ impl ScriptThread { fetch_metadata: Result<FetchMetadata, NetworkError>, ) { match fetch_metadata { - Ok(_) => {}, - Err(ref e) => warn!("Network error: {:?}", e), + Ok(_) => (), + Err(ref e) => { + warn!("Network error: {:?}", e); + }, }; + let mut incomplete_parser_contexts = self.incomplete_parser_contexts.borrow_mut(); let parser = incomplete_parser_contexts .iter_mut() @@ -3119,12 +3125,13 @@ impl ScriptThread { } } - fn handle_fetch_eof(&self, id: PipelineId, eof: Result<(), NetworkError>) { + fn handle_fetch_eof(&self, id: PipelineId, eof: Result<ResourceFetchTiming, NetworkError>) { let idx = self .incomplete_parser_contexts .borrow() .iter() .position(|&(pipeline_id, _)| pipeline_id == id); + if let Some(idx) = idx { let (_, mut ctxt) = self.incomplete_parser_contexts.borrow_mut().remove(idx); ctxt.process_response_eof(eof); @@ -3161,7 +3168,7 @@ impl ScriptThread { context.process_response(Ok(FetchMetadata::Unfiltered(meta))); context.process_response_chunk(chunk); - context.process_response_eof(Ok(())); + context.process_response_eof(Ok(ResourceFetchTiming::new(ResourceTimingType::None))); } fn handle_css_error_reporting( diff --git a/components/script/stylesheet_loader.rs b/components/script/stylesheet_loader.rs index 38957c8bf0a..88ad87ada22 100644 --- a/components/script/stylesheet_loader.rs +++ b/components/script/stylesheet_loader.rs @@ -6,13 +6,16 @@ use crate::document_loader::LoadType; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::refcounted::Trusted; use crate::dom::bindings::reflector::DomObject; +use crate::dom::bindings::root::DomRoot; use crate::dom::document::Document; use crate::dom::element::Element; use crate::dom::eventtarget::EventTarget; +use crate::dom::globalscope::GlobalScope; use crate::dom::htmlelement::HTMLElement; use crate::dom::htmllinkelement::{HTMLLinkElement, RequestGenerationId}; use crate::dom::node::{document_from_node, window_from_node}; -use crate::network_listener::{NetworkListener, PreInvoke}; +use crate::dom::performanceresourcetiming::InitiatorType; +use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener}; use cssparser::SourceLocation; use encoding_rs::UTF_8; use ipc_channel::ipc; @@ -22,6 +25,7 @@ use net_traits::request::{CorsSettings, CredentialsMode, Destination, RequestIni use net_traits::{ FetchMetadata, FetchResponseListener, FilteredMetadata, Metadata, NetworkError, ReferrerPolicy, }; +use net_traits::{ResourceFetchTiming, ResourceTimingType}; use parking_lot::RwLock; use servo_arc::Arc; use servo_url::ServoUrl; @@ -79,6 +83,7 @@ pub struct StylesheetContext { /// A token which must match the generation id of the `HTMLLinkElement` for it to load the stylesheet. /// This is ignored for `HTMLStyleElement` and imports. request_generation_id: Option<RequestGenerationId>, + resource_timing: ResourceFetchTiming, } impl PreInvoke for StylesheetContext {} @@ -108,7 +113,7 @@ impl FetchResponseListener for StylesheetContext { self.data.append(&mut payload); } - fn process_response_eof(&mut self, status: Result<(), NetworkError>) { + fn process_response_eof(&mut self, status: Result<ResourceFetchTiming, NetworkError>) { let elem = self.elem.root(); let document = self.document.root(); let mut successful = false; @@ -207,6 +212,35 @@ impl FetchResponseListener for StylesheetContext { elem.upcast::<EventTarget>().fire_event(event); } } + + fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming { + &mut self.resource_timing + } + + fn resource_timing(&self) -> &ResourceFetchTiming { + &self.resource_timing + } + + fn submit_resource_timing(&mut self) { + network_listener::submit_timing(self) + } +} + +impl ResourceTimingListener for StylesheetContext { + fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) { + let initiator_type = InitiatorType::LocalName( + self.elem + .root() + .upcast::<Element>() + .local_name() + .to_string(), + ); + (initiator_type, self.url.clone()) + } + + fn resource_timing_global(&self) -> DomRoot<GlobalScope> { + document_from_node(&*self.elem.root()).global() + } } pub struct StylesheetLoader<'a> { @@ -241,6 +275,7 @@ impl<'a> StylesheetLoader<'a> { document: Trusted::new(&*document), origin_clean: true, request_generation_id: gen, + resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource), })); let (action_sender, action_receiver) = ipc::channel().unwrap(); |