diff --git a/Cargo.lock b/Cargo.lock index dda7327330b..90675af0e77 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -340,12 +340,16 @@ version = "0.0.1" dependencies = [ "crossbeam-channel", "ipc-channel", + "libc", + "mach2", "malloc_size_of", "malloc_size_of_derive", "parking_lot", "serde", "size_of_test", + "time 0.3.36", "webrender_api", + "windows-sys 0.59.0", ] [[package]] @@ -758,6 +762,7 @@ dependencies = [ "iana-time-zone", "js-sys", "num-traits", + "serde", "wasm-bindgen", "windows-targets 0.52.6", ] @@ -1288,6 +1293,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ "powerfmt", + "serde", ] [[package]] @@ -3794,6 +3800,7 @@ dependencies = [ "servo_url", "style", "style_traits", + "time 0.3.36", "url", "webrender_api", "webrender_traits", @@ -5216,6 +5223,7 @@ dependencies = [ name = "profile" version = "0.0.1" dependencies = [ + "base", "ipc-channel", "libc", "profile_traits", @@ -5225,6 +5233,7 @@ dependencies = [ "servo_config", "task_info", "tikv-jemalloc-sys", + "time 0.3.36", ] [[package]] @@ -5235,19 +5244,21 @@ dependencies = [ "profile", "profile_traits", "servo_config", + "time 0.3.36", ] [[package]] name = "profile_traits" version = "0.0.1" dependencies = [ + "base", "crossbeam-channel", "ipc-channel", "log", "serde", "servo_config", "signpost", - "time 0.1.45", + "time 0.3.36", ] [[package]] @@ -5764,6 +5775,7 @@ dependencies = [ "tempfile", "tendril", "time 0.1.45", + "time 0.3.36", "unicode-bidi", "unicode-segmentation", "url", diff --git a/Cargo.toml b/Cargo.toml index 5a1614fbcd2..1f5ba8645c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ bluetooth_traits = { path = "components/shared/bluetooth" } byteorder = "1.5" canvas_traits = { path = "components/shared/canvas" } cfg-if = "1.0.0" -chrono = "0.4" +chrono = { version = "0.4", features = ["serde"] } compositing_traits = { path = "components/shared/compositing" } content-security-policy = { version = "0.5", features = ["serde"] } cookie = { package = "cookie", version = "0.18" } @@ -74,6 +74,7 @@ tikv-jemallocator = "0.6.0" keyboard-types = "0.7" libc = "0.2" log = "0.4" +mach2 = "0.4" malloc_size_of = { git = "https://github.com/servo/stylo", branch = "2024-07-16", features = ["servo"] } malloc_size_of_derive = "0.1" mime = "0.3.13" @@ -121,6 +122,7 @@ syn = { version = "2", default-features = false, features = ["clone-impls", "der synstructure = "0.13" thin-vec = "0.2.13" time = "0.1.41" +time_03 = { package = "time", version = "0.3", features = ["serde"] } to_shmem = { git = "https://github.com/servo/stylo", branch = "2024-07-16" } tokio = "1" tokio-rustls = "0.24" diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index 931095e38b6..2b072481f9a 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -11,6 +11,7 @@ use std::iter::once; use std::rc::Rc; use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; +use base::cross_process_instant::CrossProcessInstant; use base::id::{PipelineId, TopLevelBrowsingContextId, WebViewId}; use base::{Epoch, WebRenderEpochToU16}; use compositing_traits::{ @@ -2147,10 +2148,7 @@ impl IOCompositor { // ones that the paint metrics recorder is expecting. In that case, we get the current // time, inform layout about it and remove the pending metric from the list. if !self.pending_paint_metrics.is_empty() { - let paint_time = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap_or_default() - .as_nanos() as u64; + let paint_time = CrossProcessInstant::now(); let mut to_remove = Vec::new(); // For each pending paint metrics pipeline id for (id, pending_epoch) in &self.pending_paint_metrics { diff --git a/components/devtools/actors/network_event.rs b/components/devtools/actors/network_event.rs index c090b0f2ce1..a0ba5cb0c15 100644 --- a/components/devtools/actors/network_event.rs +++ b/components/devtools/actors/network_event.rs @@ -6,7 +6,7 @@ //! Handles interaction with the remote web console on network events (HTTP requests, responses) in Servo. use std::net::TcpStream; -use std::time::{SystemTime, UNIX_EPOCH}; +use std::time::{Duration, SystemTime, UNIX_EPOCH}; use chrono::{Local, LocalResult, TimeZone}; use devtools_traits::{HttpRequest as DevtoolsHttpRequest, HttpResponse as DevtoolsHttpResponse}; @@ -26,8 +26,8 @@ struct HttpRequest { body: Option>, started_date_time: SystemTime, time_stamp: i64, - connect_time: u64, - send_time: u64, + connect_time: Duration, + send_time: Duration, } struct HttpResponse { @@ -300,8 +300,8 @@ impl Actor for NetworkEventActor { let timings_obj = Timings { blocked: 0, dns: 0, - connect: self.request.connect_time, - send: self.request.send_time, + connect: self.request.connect_time.as_millis() as u64, + send: self.request.send_time.as_millis() as u64, wait: 0, receive: 0, }; @@ -345,8 +345,8 @@ impl NetworkEventActor { .duration_since(UNIX_EPOCH) .unwrap_or_default() .as_secs() as i64, - send_time: 0, - connect_time: 0, + send_time: Duration::ZERO, + connect_time: Duration::ZERO, }, response: HttpResponse { headers: None, @@ -493,7 +493,7 @@ impl NetworkEventActor { } } - pub fn total_time(&self) -> u64 { + pub fn total_time(&self) -> Duration { self.request.connect_time + self.request.send_time } } diff --git a/components/devtools/lib.rs b/components/devtools/lib.rs index 00690ee485f..d5802ad7154 100644 --- a/components/devtools/lib.rs +++ b/components/devtools/lib.rs @@ -515,7 +515,7 @@ fn run_server( update_type: "eventTimings".to_owned(), }; let extra = EventTimingsUpdateMsg { - total_time: actor.total_time(), + total_time: actor.total_time().as_millis() as u64, }; for stream in &mut connections { let _ = stream.write_merged_json_packet(&msg, &extra); diff --git a/components/fonts/font.rs b/components/fonts/font.rs index 809d852ef26..02cb6da8077 100644 --- a/components/fonts/font.rs +++ b/components/fonts/font.rs @@ -54,6 +54,7 @@ pub const BASE: u32 = ot_tag!('B', 'A', 'S', 'E'); pub const LAST_RESORT_GLYPH_ADVANCE: FractionalPixel = 10.0; +/// Nanoseconds spent shaping text across all layout threads. static TEXT_SHAPING_PERFORMANCE_COUNTER: AtomicUsize = AtomicUsize::new(0); // PlatformFont encapsulates access to the platform's font API, @@ -800,6 +801,7 @@ impl RunMetrics { } } +/// Get the number of nanoseconds spent shaping text across all threads. pub fn get_and_reset_text_shaping_performance_counter() -> usize { TEXT_SHAPING_PERFORMANCE_COUNTER.swap(0, Ordering::SeqCst) } diff --git a/components/layout_thread/Cargo.toml b/components/layout_thread/Cargo.toml index 14d4b263d5f..66f7b0d2a95 100644 --- a/components/layout_thread/Cargo.toml +++ b/components/layout_thread/Cargo.toml @@ -40,6 +40,7 @@ servo_config = { path = "../config" } servo_url = { path = "../url" } style = { workspace = true } style_traits = { workspace = true } +time_03 = { workspace = true } url = { workspace = true } webrender_api = { workspace = true } webrender_traits = { workspace = true } diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index 4f677655bdc..fbf2621c8a9 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -15,6 +15,7 @@ use std::process; use std::sync::{Arc, LazyLock, Mutex}; use app_units::Au; +use base::cross_process_instant::CrossProcessInstant; use base::id::{BrowsingContextId, PipelineId}; use base::Epoch; use embedder_traits::resources::{self, Resource}; @@ -101,6 +102,7 @@ use style::values::computed::font::GenericFontFamily; use style::values::computed::{FontSize, Length, NonNegativeLength}; use style::values::specified::font::KeywordInfo; use style_traits::{CSSPixel, DevicePixel, SpeculativePainter}; +use time_03::Duration; use url::Url; use webrender_api::{units, ColorF, HitTestFlags}; use webrender_traits::WebRenderScriptApi; @@ -541,7 +543,7 @@ impl Layout for LayoutThread { .collect(); } - fn set_epoch_paint_time(&mut self, epoch: Epoch, paint_time: u64) { + fn set_epoch_paint_time(&mut self, epoch: Epoch, paint_time: CrossProcessInstant) { self.paint_time_metrics.maybe_set_metric(epoch, paint_time); } } @@ -1112,13 +1114,15 @@ impl LayoutThread { }, ); // TODO(pcwalton): Measure energy usage of text shaping, perhaps? - let text_shaping_time = get_and_reset_text_shaping_performance_counter() / num_threads; + let text_shaping_time = Duration::nanoseconds( + (get_and_reset_text_shaping_performance_counter() / num_threads) as i64, + ); profile_time::send_profile_data( profile_time::ProfilerCategory::LayoutTextShaping, self.profiler_metadata(), &self.time_profiler_chan, - 0, - text_shaping_time as u64, + CrossProcessInstant::epoch(), + CrossProcessInstant::epoch() + text_shaping_time, ); // Retrieve the (possibly rebuilt) root flow. diff --git a/components/layout_thread_2020/lib.rs b/components/layout_thread_2020/lib.rs index 4038fa92952..321de57788d 100644 --- a/components/layout_thread_2020/lib.rs +++ b/components/layout_thread_2020/lib.rs @@ -16,6 +16,7 @@ use std::process; use std::sync::{Arc, LazyLock}; use app_units::Au; +use base::cross_process_instant::CrossProcessInstant; use base::id::{BrowsingContextId, PipelineId}; use base::Epoch; use embedder_traits::resources::{self, Resource}; @@ -473,7 +474,7 @@ impl Layout for LayoutThread { .collect(); } - fn set_epoch_paint_time(&mut self, epoch: Epoch, paint_time: u64) { + fn set_epoch_paint_time(&mut self, epoch: Epoch, paint_time: CrossProcessInstant) { self.paint_time_metrics.maybe_set_metric(epoch, paint_time); } } diff --git a/components/metrics/lib.rs b/components/metrics/lib.rs index 3effde8cac0..3186e35c216 100644 --- a/components/metrics/lib.rs +++ b/components/metrics/lib.rs @@ -5,8 +5,9 @@ use std::cell::{Cell, RefCell}; use std::cmp::Ordering; use std::collections::HashMap; -use std::time::{Duration, SystemTime, UNIX_EPOCH}; +use std::time::Duration; +use base::cross_process_instant::CrossProcessInstant; use base::id::PipelineId; use base::Epoch; use ipc_channel::ipc::IpcSender; @@ -22,10 +23,14 @@ pub trait ProfilerMetadataFactory { } pub trait ProgressiveWebMetric { - fn get_navigation_start(&self) -> Option; - fn set_navigation_start(&mut self, time: u64); + fn get_navigation_start(&self) -> Option; + fn set_navigation_start(&mut self, time: CrossProcessInstant); fn get_time_profiler_chan(&self) -> &ProfilerChan; - fn send_queued_constellation_msg(&self, name: ProgressiveWebMetricType, time: u64); + fn send_queued_constellation_msg( + &self, + name: ProgressiveWebMetricType, + time: CrossProcessInstant, + ); fn get_url(&self) -> &ServoUrl; } @@ -50,40 +55,36 @@ fn set_metric( metadata: Option, metric_type: ProgressiveWebMetricType, category: ProfilerCategory, - attr: &Cell>, - metric_time: Option, + attr: &Cell>, + metric_time: Option, url: &ServoUrl, ) { - let navigation_start = match pwm.get_navigation_start() { - Some(time) => time, - None => { - warn!("Trying to set metric before navigation start"); - return; - }, - }; - let now = match metric_time { - Some(time) => time, - None => SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap_or_default() - .as_nanos() as u64, - }; - let time = now - navigation_start; - attr.set(Some(time)); + let metric_time = metric_time.unwrap_or_else(CrossProcessInstant::now); + attr.set(Some(metric_time)); // Queue performance observer notification. - pwm.send_queued_constellation_msg(metric_type, time); + pwm.send_queued_constellation_msg(metric_type, metric_time); // Send the metric to the time profiler. - send_profile_data(category, metadata, pwm.get_time_profiler_chan(), time, time); + send_profile_data( + category, + metadata, + pwm.get_time_profiler_chan(), + metric_time, + metric_time, + ); // Print the metric to console if the print-pwm option was given. if opts::get().print_pwm { + let navigation_start = pwm + .get_navigation_start() + .unwrap_or_else(CrossProcessInstant::epoch); println!( - "Navigation start: {}", - pwm.get_navigation_start().unwrap().to_ms() + "{:?} {:?} {:?}", + url, + metric_type, + (metric_time - navigation_start).as_seconds_f64() ); - println!("{:?} {:?} {:?}", url, metric_type, time.to_ms()); } } @@ -96,13 +97,13 @@ fn set_metric( #[derive(MallocSizeOf)] pub struct InteractiveMetrics { /// when we navigated to the page - navigation_start: Option, + navigation_start: Option, /// indicates if the page is visually ready - dom_content_loaded: Cell>, + dom_content_loaded: Cell>, /// main thread is available -- there's been a 10s window with no tasks longer than 50ms - main_thread_available: Cell>, + main_thread_available: Cell>, // max(main_thread_available, dom_content_loaded) - time_to_interactive: Cell>, + time_to_interactive: Cell>, #[ignore_malloc_size_of = "can't measure channels"] time_profiler_chan: ProfilerChan, url: ServoUrl, @@ -110,13 +111,13 @@ pub struct InteractiveMetrics { #[derive(Clone, Copy, Debug, MallocSizeOf)] pub struct InteractiveWindow { - start: SystemTime, + start: CrossProcessInstant, } impl Default for InteractiveWindow { fn default() -> Self { Self { - start: SystemTime::now(), + start: CrossProcessInstant::now(), } } } @@ -127,29 +128,23 @@ impl InteractiveWindow { // restart: there was a task > 50ms // not all documents are interactive pub fn start_window(&mut self) { - self.start = SystemTime::now(); + self.start = CrossProcessInstant::now(); } /// check if 10s has elapsed since start pub fn needs_check(&self) -> bool { - SystemTime::now() - .duration_since(self.start) - .unwrap_or_default() >= - INTERACTIVE_WINDOW_SECONDS + CrossProcessInstant::now() - self.start > INTERACTIVE_WINDOW_SECONDS } - pub fn get_start(&self) -> u64 { + pub fn get_start(&self) -> CrossProcessInstant { self.start - .duration_since(UNIX_EPOCH) - .unwrap_or_default() - .as_nanos() as u64 } } #[derive(Debug)] pub enum InteractiveFlag { DOMContentLoaded, - TimeToInteractive(u64), + TimeToInteractive(CrossProcessInstant), } impl InteractiveMetrics { @@ -166,26 +161,22 @@ impl InteractiveMetrics { pub fn set_dom_content_loaded(&self) { if self.dom_content_loaded.get().is_none() { - self.dom_content_loaded.set(Some( - SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap_or_default() - .as_nanos() as u64, - )); + self.dom_content_loaded + .set(Some(CrossProcessInstant::now())); } } - pub fn set_main_thread_available(&self, time: u64) { + pub fn set_main_thread_available(&self, time: CrossProcessInstant) { if self.main_thread_available.get().is_none() { self.main_thread_available.set(Some(time)); } } - pub fn get_dom_content_loaded(&self) -> Option { + pub fn get_dom_content_loaded(&self) -> Option { self.dom_content_loaded.get() } - pub fn get_main_thread_available(&self) -> Option { + pub fn get_main_thread_available(&self) -> Option { self.main_thread_available.get() } @@ -225,7 +216,7 @@ impl InteractiveMetrics { ); } - pub fn get_tti(&self) -> Option { + pub fn get_tti(&self) -> Option { self.time_to_interactive.get() } @@ -235,15 +226,20 @@ impl InteractiveMetrics { } impl ProgressiveWebMetric for InteractiveMetrics { - fn get_navigation_start(&self) -> Option { + fn get_navigation_start(&self) -> Option { self.navigation_start } - fn set_navigation_start(&mut self, time: u64) { + fn set_navigation_start(&mut self, time: CrossProcessInstant) { self.navigation_start = Some(time); } - fn send_queued_constellation_msg(&self, _name: ProgressiveWebMetricType, _time: u64) {} + fn send_queued_constellation_msg( + &self, + _name: ProgressiveWebMetricType, + _time: CrossProcessInstant, + ) { + } fn get_time_profiler_chan(&self) -> &ProfilerChan { &self.time_profiler_chan @@ -257,9 +253,9 @@ impl ProgressiveWebMetric for InteractiveMetrics { // https://w3c.github.io/paint-timing/ pub struct PaintTimeMetrics { pending_metrics: RefCell, bool)>>, - navigation_start: u64, - first_paint: Cell>, - first_contentful_paint: Cell>, + navigation_start: CrossProcessInstant, + first_paint: Cell>, + first_contentful_paint: Cell>, pipeline_id: PipelineId, time_profiler_chan: ProfilerChan, constellation_chan: IpcSender, @@ -274,7 +270,7 @@ impl PaintTimeMetrics { constellation_chan: IpcSender, script_chan: IpcSender, url: ServoUrl, - navigation_start: u64, + navigation_start: CrossProcessInstant, ) -> PaintTimeMetrics { PaintTimeMetrics { pending_metrics: RefCell::new(HashMap::new()), @@ -338,7 +334,7 @@ impl PaintTimeMetrics { } } - pub fn maybe_set_metric(&self, epoch: Epoch, paint_time: u64) { + pub fn maybe_set_metric(&self, epoch: Epoch, paint_time: CrossProcessInstant) { if self.first_paint.get().is_some() && self.first_contentful_paint.get().is_some() { // If we already set all paint metrics we just bail out. return; @@ -370,25 +366,29 @@ impl PaintTimeMetrics { } } - pub fn get_first_paint(&self) -> Option { + pub fn get_first_paint(&self) -> Option { self.first_paint.get() } - pub fn get_first_contentful_paint(&self) -> Option { + pub fn get_first_contentful_paint(&self) -> Option { self.first_contentful_paint.get() } } impl ProgressiveWebMetric for PaintTimeMetrics { - fn get_navigation_start(&self) -> Option { + fn get_navigation_start(&self) -> Option { Some(self.navigation_start) } - fn set_navigation_start(&mut self, time: u64) { + fn set_navigation_start(&mut self, time: CrossProcessInstant) { self.navigation_start = time; } - fn send_queued_constellation_msg(&self, name: ProgressiveWebMetricType, time: u64) { + fn send_queued_constellation_msg( + &self, + name: ProgressiveWebMetricType, + time: CrossProcessInstant, + ) { let msg = ConstellationControlMsg::PaintMetric(self.pipeline_id, name, time); if let Err(e) = self.script_chan.send(msg) { warn!("Sending metric to script thread failed ({}).", e); diff --git a/components/net/http_loader.rs b/components/net/http_loader.rs index ce74fdaba78..fcf0857dbb5 100644 --- a/components/net/http_loader.rs +++ b/components/net/http_loader.rs @@ -9,6 +9,7 @@ use std::sync::{Arc as StdArc, Condvar, Mutex, RwLock}; use std::time::{Duration, SystemTime, UNIX_EPOCH}; use async_recursion::async_recursion; +use base::cross_process_instant::CrossProcessInstant; use base::id::{HistoryStateId, PipelineId}; use crossbeam_channel::Sender; use devtools_traits::{ @@ -116,10 +117,6 @@ impl Default for HttpState { } } -fn precise_time_ms() -> u64 { - time::precise_time_ns() / (1000 * 1000) -} - // Step 3 of https://fetch.spec.whatwg.org/#concept-fetch. pub fn set_default_accept(destination: Destination, headers: &mut HeaderMap) { if headers.contains_key(header::ACCEPT) { @@ -353,19 +350,22 @@ fn prepare_devtools_request( headers: HeaderMap, body: Option>, pipeline_id: PipelineId, - now: SystemTime, - connect_time: u64, - send_time: u64, + connect_time: Duration, + send_time: Duration, is_xhr: bool, ) -> ChromeToDevtoolsControlMsg { + let started_date_time = SystemTime::now(); let request = DevtoolsHttpRequest { url, method, headers, body, pipeline_id, - started_date_time: now, - time_stamp: now.duration_since(UNIX_EPOCH).unwrap_or_default().as_secs() as i64, + started_date_time, + time_stamp: started_date_time + .duration_since(UNIX_EPOCH) + .unwrap_or_default() + .as_secs() as i64, connect_time, send_time, is_xhr, @@ -614,7 +614,7 @@ async fn obtain_response( // TODO(#21261) connect_start: set if a persistent connection is *not* used and the last non-redirected // fetch passes the timing allow check - let connect_start = precise_time_ms(); + let connect_start = CrossProcessInstant::now(); context .timing .lock() @@ -638,7 +638,7 @@ async fn obtain_response( }; *request.headers_mut() = headers.clone(); - let connect_end = precise_time_ms(); + let connect_end = CrossProcessInstant::now(); context .timing .lock() @@ -649,7 +649,7 @@ async fn obtain_response( let pipeline_id = *pipeline_id; let closure_url = url.clone(); let method = method.clone(); - let send_start = precise_time_ms(); + let send_start = CrossProcessInstant::now(); let host = request.uri().host().unwrap_or("").to_owned(); let override_manager = context.state.override_manager.clone(); @@ -658,7 +658,7 @@ async fn obtain_response( client .request(request) .and_then(move |res| { - let send_end = precise_time_ms(); + let send_end = CrossProcessInstant::now(); // TODO(#21271) response_start: immediately after receiving first byte of response @@ -671,9 +671,8 @@ async fn obtain_response( headers, Some(devtools_bytes.lock().unwrap().clone()), pipeline_id, - SystemTime::now(), - connect_end - connect_start, - send_end - send_start, + (connect_end - connect_start).unsigned_abs(), + (send_end - send_start).unsigned_abs(), is_xhr, )) // TODO: ^This is not right, connect_start is taken before contructing the diff --git a/components/profile/Cargo.toml b/components/profile/Cargo.toml index eacd0b55d0e..ce8c801d108 100644 --- a/components/profile/Cargo.toml +++ b/components/profile/Cargo.toml @@ -11,11 +11,13 @@ name = "profile" path = "lib.rs" [dependencies] +base = { workspace = true } ipc-channel = { workspace = true } profile_traits = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } servo_config = { path = "../config" } +time_03 = { workspace = true } [target.'cfg(target_os = "macos")'.dependencies] task_info = { path = "../../support/rust-task_info" } diff --git a/components/profile/mem.rs b/components/profile/mem.rs index 5d791d5e6e2..0471aadbe0f 100644 --- a/components/profile/mem.rs +++ b/components/profile/mem.rs @@ -8,7 +8,7 @@ use std::borrow::ToOwned; use std::cmp::Ordering; use std::collections::HashMap; use std::thread; -use std::time::Instant; +use std::time::{Duration, Instant}; use ipc_channel::ipc::{self, IpcReceiver}; use ipc_channel::router::ROUTER; @@ -17,8 +17,6 @@ use profile_traits::mem::{ }; use profile_traits::path; -use crate::time::duration_from_seconds; - pub struct Profiler { /// The port through which messages are received. pub port: IpcReceiver, @@ -43,7 +41,7 @@ impl Profiler { thread::Builder::new() .name("MemoryProfTimer".to_owned()) .spawn(move || loop { - thread::sleep(duration_from_seconds(period)); + thread::sleep(Duration::from_secs_f64(period)); if chan.send(ProfilerMsg::Print).is_err() { break; } diff --git a/components/profile/time.rs b/components/profile/time.rs index 836cded25d4..9aaa4ef0023 100644 --- a/components/profile/time.rs +++ b/components/profile/time.rs @@ -10,7 +10,6 @@ use std::fs::File; use std::io::{self, Write}; use std::path::Path; use std::thread; -use std::time::Duration; use ipc_channel::ipc::{self, IpcReceiver}; use profile_traits::time::{ @@ -18,6 +17,7 @@ use profile_traits::time::{ TimerMetadataFrameType, TimerMetadataReflowType, }; use servo_config::opts::OutputOptions; +use time_03::Duration; use crate::trace_dump::TraceDump; @@ -156,7 +156,7 @@ impl Formattable for ProfilerCategory { } } -type ProfilerBuckets = BTreeMap<(ProfilerCategory, Option), Vec>; +type ProfilerBuckets = BTreeMap<(ProfilerCategory, Option), Vec>; // back end of the profiler that handles data aggregation and performance metrics pub struct Profiler { @@ -192,7 +192,7 @@ impl Profiler { thread::Builder::new() .name("TimeProfTimer".to_owned()) .spawn(move || loop { - thread::sleep(duration_from_seconds(period)); + thread::sleep(std::time::Duration::from_secs_f64(period)); if chan.send(ProfilerMsg::Print).is_err() { break; } @@ -258,18 +258,17 @@ impl Profiler { } } - fn find_or_insert(&mut self, k: (ProfilerCategory, Option), t: f64) { - self.buckets.entry(k).or_default().push(t); + fn find_or_insert(&mut self, k: (ProfilerCategory, Option), duration: Duration) { + self.buckets.entry(k).or_default().push(duration); } fn handle_msg(&mut self, msg: ProfilerMsg) -> bool { match msg.clone() { - ProfilerMsg::Time(k, t) => { + ProfilerMsg::Time(category_and_metadata, (start_time, end_time)) => { if let Some(ref mut trace) = self.trace { - trace.write_one(&k, t); + trace.write_one(&category_and_metadata, start_time, end_time); } - let ms = (t.1 - t.0) as f64 / 1000000f64; - self.find_or_insert(k, ms); + self.find_or_insert(category_and_metadata, end_time - start_time); }, ProfilerMsg::Print => { if let Some(ProfilerMsg::Time(..)) = self.last_msg { @@ -300,16 +299,16 @@ impl Profiler { } /// Get tuple (mean, median, min, max) for profiler statistics. - pub fn get_statistics(data: &[f64]) -> (f64, f64, f64, f64) { - data.iter().fold(-f64::INFINITY, |a, &b| { - debug_assert!(a <= b, "Data must be sorted"); - b - }); + pub fn get_statistics(data: &[Duration]) -> (Duration, Duration, Duration, Duration) { + debug_assert!( + data.windows(2).all(|window| window[0] <= window[1]), + "Data must be sorted" + ); let data_len = data.len(); debug_assert!(data_len > 0); let (mean, median, min, max) = ( - data.iter().sum::() / (data_len as f64), + data.iter().sum::() / data_len as u32, data[data_len / 2], data[0], data[data_len - 1], @@ -341,10 +340,10 @@ impl Profiler { "{}\t{}\t{:15.4}\t{:15.4}\t{:15.4}\t{:15.4}\t{:15}", category.format(&self.output), meta.format(&self.output), - mean, - median, - min, - max, + mean.as_seconds_f64() * 1000., + median.as_seconds_f64() * 1000., + min.as_seconds_f64() * 1000., + max.as_seconds_f64() * 1000., data_len ) .unwrap(); @@ -384,10 +383,10 @@ impl Profiler { "{:-35}{} {:15.4} {:15.4} {:15.4} {:15.4} {:15}", category.format(&self.output), meta.format(&self.output), - mean, - median, - min, - max, + mean.as_seconds_f64() * 1000., + median.as_seconds_f64() * 1000., + min.as_seconds_f64() * 1000., + max.as_seconds_f64() * 1000., data_len ) .unwrap(); @@ -405,17 +404,3 @@ impl Profiler { }; } } - -pub fn duration_from_seconds(secs: f64) -> Duration { - pub const NANOS_PER_SEC: u32 = 1_000_000_000; - - // Get number of seconds and check that it fits in a u64. - let whole_secs = secs.trunc(); - assert!(whole_secs >= 0.0 && whole_secs <= u64::MAX as f64); - - // Get number of nanoseconds. This should always fit in a u32, but check anyway. - let nanos = (secs.fract() * (NANOS_PER_SEC as f64)).trunc(); - assert!(nanos >= 0.0 && nanos <= u32::MAX as f64); - - Duration::new(whole_secs as u64, nanos as u32) -} diff --git a/components/profile/trace_dump.rs b/components/profile/trace_dump.rs index 66614a28b98..07c5569eb43 100644 --- a/components/profile/trace_dump.rs +++ b/components/profile/trace_dump.rs @@ -7,6 +7,7 @@ use std::io::{self, Write}; use std::{fs, path}; +use base::cross_process_instant::CrossProcessInstant; use profile_traits::time::{ProfilerCategory, TimerMetadata}; use serde::Serialize; @@ -44,13 +45,14 @@ impl TraceDump { pub fn write_one( &mut self, category: &(ProfilerCategory, Option), - time: (u64, u64), + start_time: CrossProcessInstant, + end_time: CrossProcessInstant, ) { let entry = TraceEntry { category: category.0, metadata: category.1.clone(), - start_time: time.0, - end_time: time.1, + start_time: (start_time - CrossProcessInstant::epoch()).whole_nanoseconds() as u64, + end_time: (end_time - CrossProcessInstant::epoch()).whole_nanoseconds() as u64, }; serde_json::to_writer(&mut self.file, &entry).unwrap(); writeln!(&mut self.file, ",").unwrap(); diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml index d30147adea5..e6384f69606 100644 --- a/components/script/Cargo.toml +++ b/components/script/Cargo.toml @@ -105,6 +105,7 @@ swapper = "0.1" tempfile = "3" tendril = { version = "0.4.1", features = ["encoding_rs"] } time = { workspace = true } +time_03 = { workspace = true } unicode-bidi = { workspace = true } unicode-segmentation = { workspace = true } url = { workspace = true } diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 0b616866245..77cc9e534f7 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -14,6 +14,7 @@ use std::slice::from_ref; use std::sync::LazyLock; use std::time::{Duration, Instant}; +use base::cross_process_instant::CrossProcessInstant; use base::id::BrowsingContextId; use canvas_traits::webgl::{self, WebGLContextId, WebGLMsg}; use content_security_policy::{self as csp, CspList}; @@ -342,16 +343,24 @@ pub struct Document { active_touch_points: DomRefCell>>, /// Navigation Timing properties: /// - dom_loading: Cell, - dom_interactive: Cell, - dom_content_loaded_event_start: Cell, - dom_content_loaded_event_end: Cell, - dom_complete: Cell, - top_level_dom_complete: Cell, - load_event_start: Cell, - load_event_end: Cell, - unload_event_start: Cell, - unload_event_end: Cell, + #[no_trace] + dom_interactive: Cell>, + #[no_trace] + dom_content_loaded_event_start: Cell>, + #[no_trace] + dom_content_loaded_event_end: Cell>, + #[no_trace] + dom_complete: Cell>, + #[no_trace] + top_level_dom_complete: Cell>, + #[no_trace] + load_event_start: Cell>, + #[no_trace] + load_event_end: Cell>, + #[no_trace] + unload_event_start: Cell>, + #[no_trace] + unload_event_end: Cell>, /// #[no_trace] https_state: Cell, @@ -1068,15 +1077,14 @@ impl Document { self.send_to_embedder(EmbedderMsg::LoadStart); self.send_to_embedder(EmbedderMsg::Status(None)); } - update_with_current_time_ms(&self.dom_loading); }, DocumentReadyState::Complete => { if self.window().is_top_level() { self.send_to_embedder(EmbedderMsg::LoadComplete); } - update_with_current_time_ms(&self.dom_complete); + update_with_current_instant(&self.dom_complete); }, - DocumentReadyState::Interactive => update_with_current_time_ms(&self.dom_interactive), + DocumentReadyState::Interactive => update_with_current_instant(&self.dom_interactive), }; self.ready_state.set(state); @@ -2153,8 +2161,8 @@ impl Document { let loader = self.loader.borrow(); // Servo measures when the top-level content (not iframes) is loaded. - if (self.top_level_dom_complete.get() == 0) && loader.is_only_blocked_by_iframes() { - update_with_current_time_ms(&self.top_level_dom_complete); + if self.top_level_dom_complete.get().is_none() && loader.is_only_blocked_by_iframes() { + update_with_current_instant(&self.top_level_dom_complete); } if loader.is_blocked() || loader.events_inhibited() { @@ -2347,7 +2355,7 @@ impl Document { event.set_trusted(true); // http://w3c.github.io/navigation-timing/#widl-PerformanceNavigationTiming-loadEventStart - update_with_current_time_ms(&document.load_event_start); + update_with_current_instant(&document.load_event_start); debug!("About to dispatch load for {:?}", document.url()); // FIXME(nox): Why are errors silenced here? @@ -2356,7 +2364,7 @@ impl Document { ); // http://w3c.github.io/navigation-timing/#widl-PerformanceNavigationTiming-loadEventEnd - update_with_current_time_ms(&document.load_event_end); + update_with_current_instant(&document.load_event_end); if let Some(fragment) = document.url().fragment() { document.check_and_scroll_fragment(fragment); @@ -2592,7 +2600,7 @@ impl Document { "Complete before DOMContentLoaded?" ); - update_with_current_time_ms(&self.dom_content_loaded_event_start); + update_with_current_instant(&self.dom_content_loaded_event_start); // Step 4.1. let window = self.window(); @@ -2604,7 +2612,7 @@ impl Document { task!(fire_dom_content_loaded_event: move || { let document = document.root(); document.upcast::().fire_bubbling_event(atom!("DOMContentLoaded")); - update_with_current_time_ms(&document.dom_content_loaded_event_end); + update_with_current_instant(&document.dom_content_loaded_event_end); }), window.upcast(), ) @@ -2690,15 +2698,11 @@ impl Document { .find(|node| node.browsing_context_id() == Some(browsing_context_id)) } - pub fn get_dom_loading(&self) -> u64 { - self.dom_loading.get() - } - - pub fn get_dom_interactive(&self) -> u64 { + pub fn get_dom_interactive(&self) -> Option { self.dom_interactive.get() } - pub fn set_navigation_start(&self, navigation_start: u64) { + pub fn set_navigation_start(&self, navigation_start: CrossProcessInstant) { self.interactive_time .borrow_mut() .set_navigation_start(navigation_start); @@ -2712,35 +2716,35 @@ impl Document { self.get_interactive_metrics().get_tti().is_some() } - pub fn get_dom_content_loaded_event_start(&self) -> u64 { + pub fn get_dom_content_loaded_event_start(&self) -> Option { self.dom_content_loaded_event_start.get() } - pub fn get_dom_content_loaded_event_end(&self) -> u64 { + pub fn get_dom_content_loaded_event_end(&self) -> Option { self.dom_content_loaded_event_end.get() } - pub fn get_dom_complete(&self) -> u64 { + pub fn get_dom_complete(&self) -> Option { self.dom_complete.get() } - pub fn get_top_level_dom_complete(&self) -> u64 { + pub fn get_top_level_dom_complete(&self) -> Option { self.top_level_dom_complete.get() } - pub fn get_load_event_start(&self) -> u64 { + pub fn get_load_event_start(&self) -> Option { self.load_event_start.get() } - pub fn get_load_event_end(&self) -> u64 { + pub fn get_load_event_end(&self) -> Option { self.load_event_end.get() } - pub fn get_unload_event_start(&self) -> u64 { + pub fn get_unload_event_start(&self) -> Option { self.unload_event_start.get() } - pub fn get_unload_event_end(&self) -> u64 { + pub fn get_unload_event_end(&self) -> Option { self.unload_event_end.get() } @@ -3236,7 +3240,6 @@ impl Document { pending_restyles: DomRefCell::new(HashMap::new()), needs_paint: Cell::new(false), active_touch_points: DomRefCell::new(Vec::new()), - dom_loading: Cell::new(Default::default()), dom_interactive: Cell::new(Default::default()), dom_content_loaded_event_start: Cell::new(Default::default()), dom_content_loaded_event_end: Cell::new(Default::default()), @@ -4101,11 +4104,8 @@ impl Document { self.visibility_state.set(visibility_state); // Step 3 Queue a new VisibilityStateEntry whose visibility state is visibilityState and whose timestamp is // the current high resolution time given document's relevant global object. - let entry = VisibilityStateEntry::new( - &self.global(), - visibility_state, - *self.global().performance().Now(), - ); + let entry = + VisibilityStateEntry::new(&self.global(), visibility_state, CrossProcessInstant::now()); self.window .Performance() .queue_entry(entry.upcast::()); @@ -5441,11 +5441,9 @@ impl DocumentMethods for Document { } } -fn update_with_current_time_ms(marker: &Cell) { - if marker.get() == 0 { - let time = time::get_time(); - let current_time_ms = time.sec * 1000 + time.nsec as i64 / 1000000; - marker.set(current_time_ms as u64); +fn update_with_current_instant(marker: &Cell>) { + if marker.get().is_none() { + marker.set(Some(CrossProcessInstant::now())) } } diff --git a/components/script/dom/event.rs b/components/script/dom/event.rs index e156a07ff68..83f34d3316a 100644 --- a/components/script/dom/event.rs +++ b/components/script/dom/event.rs @@ -5,10 +5,10 @@ use std::cell::Cell; use std::default::Default; +use base::cross_process_instant::CrossProcessInstant; use devtools_traits::{TimelineMarker, TimelineMarkerType}; use dom_struct::dom_struct; use js::rust::HandleObject; -use metrics::ToMs; use servo_atoms::Atom; use crate::dom::bindings::callback::ExceptionHandling; @@ -16,7 +16,6 @@ use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::codegen::Bindings::EventBinding; use crate::dom::bindings::codegen::Bindings::EventBinding::{EventConstants, EventMethods}; use crate::dom::bindings::codegen::Bindings::PerformanceBinding::DOMHighResTimeStamp; -use crate::dom::bindings::codegen::Bindings::PerformanceBinding::Performance_Binding::PerformanceMethods; use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use crate::dom::bindings::error::Fallible; use crate::dom::bindings::inheritance::Castable; @@ -31,7 +30,6 @@ use crate::dom::globalscope::GlobalScope; use crate::dom::htmlinputelement::InputActivationState; use crate::dom::mouseevent::MouseEvent; use crate::dom::node::{Node, ShadowIncluding}; -use crate::dom::performance::reduce_timing_resolution; use crate::dom::virtualmethods::vtable_for; use crate::dom::window::Window; use crate::script_runtime::CanGc; @@ -53,7 +51,8 @@ pub struct Event { trusted: Cell, dispatching: Cell, initialized: Cell, - precise_time_ns: u64, + #[no_trace] + time_stamp: CrossProcessInstant, } impl Event { @@ -72,7 +71,7 @@ impl Event { trusted: Cell::new(false), dispatching: Cell::new(false), initialized: Cell::new(false), - precise_time_ns: time::precise_time_ns(), + time_stamp: CrossProcessInstant::now(), } } @@ -498,10 +497,9 @@ impl EventMethods for Event { /// fn TimeStamp(&self) -> DOMHighResTimeStamp { - reduce_timing_resolution( - (self.precise_time_ns - (*self.global().performance().TimeOrigin()).round() as u64) - .to_ms(), - ) + self.global() + .performance() + .to_dom_high_res_time_stamp(self.time_stamp) } /// diff --git a/components/script/dom/macros.rs b/components/script/dom/macros.rs index 30bb6bcfe4f..6bece48b720 100644 --- a/components/script/dom/macros.rs +++ b/components/script/dom/macros.rs @@ -579,6 +579,9 @@ macro_rules! rooted_vec { /// DOM struct implementation for simple interfaces inheriting from PerformanceEntry. macro_rules! impl_performance_entry_struct( ($binding:ident, $struct:ident, $type:expr) => ( + use base::cross_process_instant::CrossProcessInstant; + use time_03::Duration; + use crate::dom::bindings::reflector::reflect_dom_object; use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::str::DOMString; @@ -592,12 +595,12 @@ macro_rules! impl_performance_entry_struct( } impl $struct { - fn new_inherited(name: DOMString, start_time: f64, duration: f64) + fn new_inherited(name: DOMString, start_time: CrossProcessInstant, duration: Duration) -> $struct { $struct { entry: PerformanceEntry::new_inherited(name, DOMString::from($type), - start_time, + Some(start_time), duration) } } @@ -605,8 +608,8 @@ macro_rules! impl_performance_entry_struct( #[allow(crown::unrooted_must_root)] pub fn new(global: &GlobalScope, name: DOMString, - start_time: f64, - duration: f64) -> DomRoot<$struct> { + start_time: CrossProcessInstant, + duration: Duration) -> DomRoot<$struct> { let entry = $struct::new_inherited(name, start_time, duration); reflect_dom_object(Box::new(entry), global) } diff --git a/components/script/dom/performance.rs b/components/script/dom/performance.rs index 277662ccb42..983e40ca23a 100644 --- a/components/script/dom/performance.rs +++ b/components/script/dom/performance.rs @@ -6,8 +6,9 @@ use std::cell::Cell; use std::cmp::Ordering; use std::collections::VecDeque; +use base::cross_process_instant::CrossProcessInstant; use dom_struct::dom_struct; -use metrics::ToMs; +use time_03::Duration; use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::codegen::Bindings::PerformanceBinding::{ @@ -105,16 +106,12 @@ impl PerformanceEntryList { &self, name: DOMString, entry_type: DOMString, - ) -> f64 { - match self - .entries + ) -> Option { + self.entries .iter() .rev() .find(|e| *e.entry_type() == *entry_type && *e.name() == *name) - { - Some(entry) => entry.start_time(), - None => 0., - } + .and_then(|entry| entry.start_time()) } } @@ -139,7 +136,10 @@ pub struct Performance { buffer: DomRefCell, observers: DomRefCell>, pending_notification_observers_task: Cell, - navigation_start_precise: u64, + #[no_trace] + /// The `timeOrigin` as described in + /// . + time_origin: CrossProcessInstant, /// /// The max-size of the buffer, set to 0 once the pipeline exits. /// TODO: have one max-size per entry type. @@ -150,13 +150,13 @@ pub struct Performance { } impl Performance { - fn new_inherited(navigation_start_precise: u64) -> Performance { + fn new_inherited(time_origin: CrossProcessInstant) -> Performance { Performance { eventtarget: EventTarget::new_inherited(), buffer: DomRefCell::new(PerformanceEntryList::new(Vec::new())), observers: DomRefCell::new(Vec::new()), pending_notification_observers_task: Cell::new(false), - navigation_start_precise, + time_origin, resource_timing_buffer_size_limit: Cell::new(250), resource_timing_buffer_current_size: Cell::new(0), resource_timing_buffer_pending_full_event: Cell::new(false), @@ -164,13 +164,30 @@ impl Performance { } } - pub fn new(global: &GlobalScope, navigation_start_precise: u64) -> DomRoot { + pub fn new( + global: &GlobalScope, + navigation_start: CrossProcessInstant, + ) -> DomRoot { reflect_dom_object( - Box::new(Performance::new_inherited(navigation_start_precise)), + Box::new(Performance::new_inherited(navigation_start)), global, ) } + pub(crate) fn to_dom_high_res_time_stamp( + &self, + instant: CrossProcessInstant, + ) -> DOMHighResTimeStamp { + (instant - self.time_origin).to_dom_high_res_time_stamp() + } + + pub(crate) fn maybe_to_dom_high_res_time_stamp( + &self, + instant: Option, + ) -> DOMHighResTimeStamp { + self.to_dom_high_res_time_stamp(instant.unwrap_or(self.time_origin)) + } + /// Clear all buffered performance entries, and disable the buffer. /// Called as part of the window's "clear_js_runtime" workflow, /// performed when exiting a pipeline. @@ -329,10 +346,6 @@ impl Performance { } } - fn now(&self) -> f64 { - (time::precise_time_ns() - self.navigation_start_precise).to_ms() - } - fn can_add_resource_timing_entry(&self) -> bool { self.resource_timing_buffer_current_size.get() <= self.resource_timing_buffer_size_limit.get() @@ -423,12 +436,12 @@ impl PerformanceMethods for Performance { // https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/HighResolutionTime/Overview.html#dom-performance-now fn Now(&self) -> DOMHighResTimeStamp { - reduce_timing_resolution(self.now()) + self.to_dom_high_res_time_stamp(CrossProcessInstant::now()) } // https://www.w3.org/TR/hr-time-2/#dom-performance-timeorigin fn TimeOrigin(&self) -> DOMHighResTimeStamp { - reduce_timing_resolution(self.navigation_start_precise as f64) + (self.time_origin - CrossProcessInstant::epoch()).to_dom_high_res_time_stamp() } // https://www.w3.org/TR/performance-timeline-2/#dom-performance-getentries @@ -465,7 +478,12 @@ impl PerformanceMethods for Performance { } // Steps 2 to 6. - let entry = PerformanceMark::new(&global, mark_name, self.now(), 0.); + let entry = PerformanceMark::new( + &global, + mark_name, + CrossProcessInstant::now(), + Duration::ZERO, + ); // Steps 7 and 8. self.queue_entry(entry.upcast::()); @@ -488,22 +506,23 @@ impl PerformanceMethods for Performance { end_mark: Option, ) -> Fallible<()> { // Steps 1 and 2. - let end_time = match end_mark { - Some(name) => self - .buffer - .borrow() - .get_last_entry_start_time_with_name_and_type(DOMString::from("mark"), name), - None => self.now(), - }; + let end_time = end_mark + .map(|name| { + self.buffer + .borrow() + .get_last_entry_start_time_with_name_and_type(DOMString::from("mark"), name) + .unwrap_or(self.time_origin) + }) + .unwrap_or_else(CrossProcessInstant::now); // Step 3. - let start_time = match start_mark { - Some(name) => self - .buffer - .borrow() - .get_last_entry_start_time_with_name_and_type(DOMString::from("mark"), name), - None => 0., - }; + let start_time = start_mark + .and_then(|name| { + self.buffer + .borrow() + .get_last_entry_start_time_with_name_and_type(DOMString::from("mark"), name) + }) + .unwrap_or(self.time_origin); // Steps 4 to 8. let entry = PerformanceMeasure::new( @@ -548,13 +567,18 @@ impl PerformanceMethods for Performance { ); } -// https://www.w3.org/TR/hr-time-2/#clock-resolution -pub fn reduce_timing_resolution(exact: f64) -> DOMHighResTimeStamp { - // We need a granularity no finer than 5 microseconds. - // 5 microseconds isn't an exactly representable f64 so WPT tests - // might occasionally corner-case on rounding. - // web-platform-tests/wpt#21526 wants us to use an integer number of - // microseconds; the next divisor of milliseconds up from 5 microseconds - // is 10, which is 1/100th of a millisecond. - Finite::wrap((exact * 100.0).floor() / 100.0) +pub(crate) trait ToDOMHighResTimeStamp { + fn to_dom_high_res_time_stamp(&self) -> DOMHighResTimeStamp; +} + +impl ToDOMHighResTimeStamp for Duration { + fn to_dom_high_res_time_stamp(&self) -> DOMHighResTimeStamp { + // https://www.w3.org/TR/hr-time-2/#clock-resolution + // We need a granularity no finer than 5 microseconds. 5 microseconds isn't an + // exactly representable f64 so WPT tests might occasionally corner-case on + // rounding. web-platform-tests/wpt#21526 wants us to use an integer number of + // microseconds; the next divisor of milliseconds up from 5 microseconds is 10. + let microseconds_rounded = (self.whole_microseconds() as f64 / 10.).floor() * 10.; + Finite::wrap(microseconds_rounded / 1000.) + } } diff --git a/components/script/dom/performanceentry.rs b/components/script/dom/performanceentry.rs index 1624a7f15fa..46f13d71515 100644 --- a/components/script/dom/performanceentry.rs +++ b/components/script/dom/performanceentry.rs @@ -2,31 +2,38 @@ * 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 base::cross_process_instant::CrossProcessInstant; use dom_struct::dom_struct; +use time_03::Duration; +use super::performance::ToDOMHighResTimeStamp; use crate::dom::bindings::codegen::Bindings::PerformanceBinding::DOMHighResTimeStamp; use crate::dom::bindings::codegen::Bindings::PerformanceEntryBinding::PerformanceEntryMethods; -use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; +use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector}; use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::str::DOMString; use crate::dom::globalscope::GlobalScope; -use crate::dom::performance::reduce_timing_resolution; #[dom_struct] pub struct PerformanceEntry { reflector_: Reflector, name: DOMString, entry_type: DOMString, - start_time: f64, - duration: f64, + #[no_trace] + start_time: Option, + /// The duration of this [`PerformanceEntry`]. This is a [`time_03::Duration`], + /// because it can be negative and `std::time::Duration` cannot be. + #[no_trace] + #[ignore_malloc_size_of = "No MallocSizeOf support for `time` crate"] + duration: Duration, } impl PerformanceEntry { pub fn new_inherited( name: DOMString, entry_type: DOMString, - start_time: f64, - duration: f64, + start_time: Option, + duration: Duration, ) -> PerformanceEntry { PerformanceEntry { reflector_: Reflector::new(), @@ -42,10 +49,10 @@ impl PerformanceEntry { global: &GlobalScope, name: DOMString, entry_type: DOMString, - start_time: f64, - duration: f64, + start_time: CrossProcessInstant, + duration: Duration, ) -> DomRoot { - let entry = PerformanceEntry::new_inherited(name, entry_type, start_time, duration); + let entry = PerformanceEntry::new_inherited(name, entry_type, Some(start_time), duration); reflect_dom_object(Box::new(entry), global) } @@ -57,11 +64,11 @@ impl PerformanceEntry { &self.name } - pub fn start_time(&self) -> f64 { + pub fn start_time(&self) -> Option { self.start_time } - pub fn duration(&self) -> f64 { + pub fn duration(&self) -> Duration { self.duration } } @@ -79,11 +86,13 @@ impl PerformanceEntryMethods for PerformanceEntry { // https://w3c.github.io/performance-timeline/#dom-performanceentry-starttime fn StartTime(&self) -> DOMHighResTimeStamp { - reduce_timing_resolution(self.start_time) + self.global() + .performance() + .maybe_to_dom_high_res_time_stamp(self.start_time) } // https://w3c.github.io/performance-timeline/#dom-performanceentry-duration fn Duration(&self) -> DOMHighResTimeStamp { - reduce_timing_resolution(self.duration) + self.duration.to_dom_high_res_time_stamp() } } diff --git a/components/script/dom/performancenavigationtiming.rs b/components/script/dom/performancenavigationtiming.rs index f976e7320de..b4489fa8122 100644 --- a/components/script/dom/performancenavigationtiming.rs +++ b/components/script/dom/performancenavigationtiming.rs @@ -2,13 +2,14 @@ * 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 base::cross_process_instant::CrossProcessInstant; use dom_struct::dom_struct; use crate::dom::bindings::codegen::Bindings::PerformanceBinding::DOMHighResTimeStamp; use crate::dom::bindings::codegen::Bindings::PerformanceNavigationTimingBinding::{ NavigationTimingType, PerformanceNavigationTimingMethods, }; -use crate::dom::bindings::num::Finite; +use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::reflector::reflect_dom_object; use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::document::Document; @@ -22,16 +23,13 @@ use crate::dom::performanceresourcetiming::{InitiatorType, PerformanceResourceTi pub struct PerformanceNavigationTiming { // https://w3c.github.io/navigation-timing/#PerformanceResourceTiming performanceresourcetiming: PerformanceResourceTiming, - navigation_start: u64, - navigation_start_precise: u64, document: Dom, nav_type: NavigationTimingType, } impl PerformanceNavigationTiming { fn new_inherited( - nav_start: u64, - nav_start_precise: u64, + navigation_start: CrossProcessInstant, document: &Document, ) -> PerformanceNavigationTiming { PerformanceNavigationTiming { @@ -39,10 +37,8 @@ impl PerformanceNavigationTiming { document.url(), InitiatorType::Navigation, None, - nav_start_precise as f64, + Some(navigation_start), ), - navigation_start: nav_start, - navigation_start_precise: nav_start_precise, document: Dom::from_ref(document), nav_type: NavigationTimingType::Navigate, } @@ -50,14 +46,12 @@ impl PerformanceNavigationTiming { pub fn new( global: &GlobalScope, - nav_start: u64, - nav_start_precise: u64, + fetch_start: CrossProcessInstant, document: &Document, ) -> DomRoot { reflect_dom_object( Box::new(PerformanceNavigationTiming::new_inherited( - nav_start, - nav_start_precise, + fetch_start, document, )), global, @@ -69,42 +63,50 @@ impl PerformanceNavigationTiming { 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) + self.upcast::() + .to_dom_high_res_time_stamp(self.document.get_unload_event_start()) } // https://w3c.github.io/navigation-timing/#dom-performancenavigationtiming-unloadeventend fn UnloadEventEnd(&self) -> DOMHighResTimeStamp { - Finite::wrap(self.document.get_unload_event_end() as f64) + self.upcast::() + .to_dom_high_res_time_stamp(self.document.get_unload_event_end()) } // https://w3c.github.io/navigation-timing/#dom-performancenavigationtiming-dominteractive fn DomInteractive(&self) -> DOMHighResTimeStamp { - Finite::wrap(self.document.get_dom_interactive() as f64) + self.upcast::() + .to_dom_high_res_time_stamp(self.document.get_dom_interactive()) } // 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) + self.upcast::() + .to_dom_high_res_time_stamp(self.document.get_dom_content_loaded_event_start()) } // 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) + self.upcast::() + .to_dom_high_res_time_stamp(self.document.get_dom_content_loaded_event_end()) } // https://w3c.github.io/navigation-timing/#dom-performancenavigationtiming-domcomplete fn DomComplete(&self) -> DOMHighResTimeStamp { - Finite::wrap(self.document.get_dom_complete() as f64) + self.upcast::() + .to_dom_high_res_time_stamp(self.document.get_dom_complete()) } // https://w3c.github.io/navigation-timing/#dom-performancenavigationtiming-loadeventstart fn LoadEventStart(&self) -> DOMHighResTimeStamp { - Finite::wrap(self.document.get_load_event_start() as f64) + self.upcast::() + .to_dom_high_res_time_stamp(self.document.get_load_event_start()) } // https://w3c.github.io/navigation-timing/#dom-performancenavigationtiming-loadeventend fn LoadEventEnd(&self) -> DOMHighResTimeStamp { - Finite::wrap(self.document.get_load_event_end() as f64) + self.upcast::() + .to_dom_high_res_time_stamp(self.document.get_load_event_end()) } // https://w3c.github.io/navigation-timing/#dom-performancenavigationtiming-type @@ -120,6 +122,7 @@ impl PerformanceNavigationTimingMethods for PerformanceNavigationTiming { // 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) + self.upcast::() + .to_dom_high_res_time_stamp(self.document.get_top_level_dom_complete()) } } diff --git a/components/script/dom/performancepainttiming.rs b/components/script/dom/performancepainttiming.rs index 69fa440d744..4226ab91960 100644 --- a/components/script/dom/performancepainttiming.rs +++ b/components/script/dom/performancepainttiming.rs @@ -2,9 +2,10 @@ * 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 base::cross_process_instant::CrossProcessInstant; use dom_struct::dom_struct; -use metrics::ToMs; use script_traits::ProgressiveWebMetricType; +use time_03::Duration; use crate::dom::bindings::reflector::reflect_dom_object; use crate::dom::bindings::root::DomRoot; @@ -20,7 +21,7 @@ pub struct PerformancePaintTiming { impl PerformancePaintTiming { fn new_inherited( metric_type: ProgressiveWebMetricType, - start_time: u64, + start_time: CrossProcessInstant, ) -> PerformancePaintTiming { let name = match metric_type { ProgressiveWebMetricType::FirstPaint => DOMString::from("first-paint"), @@ -33,8 +34,8 @@ impl PerformancePaintTiming { entry: PerformanceEntry::new_inherited( name, DOMString::from("paint"), - start_time.to_ms(), - 0., + Some(start_time), + Duration::ZERO, ), } } @@ -43,7 +44,7 @@ impl PerformancePaintTiming { pub fn new( global: &GlobalScope, metric_type: ProgressiveWebMetricType, - start_time: u64, + start_time: CrossProcessInstant, ) -> DomRoot { let entry = PerformancePaintTiming::new_inherited(metric_type, start_time); reflect_dom_object(Box::new(entry), global) diff --git a/components/script/dom/performanceresourcetiming.rs b/components/script/dom/performanceresourcetiming.rs index 3afaef5f912..ac7791f9fed 100644 --- a/components/script/dom/performanceresourcetiming.rs +++ b/components/script/dom/performanceresourcetiming.rs @@ -2,17 +2,18 @@ * 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 base::cross_process_instant::CrossProcessInstant; use dom_struct::dom_struct; use net_traits::ResourceFetchTiming; use servo_url::ServoUrl; +use time_03::Duration; use crate::dom::bindings::codegen::Bindings::PerformanceBinding::DOMHighResTimeStamp; use crate::dom::bindings::codegen::Bindings::PerformanceResourceTimingBinding::PerformanceResourceTimingMethods; -use crate::dom::bindings::reflector::reflect_dom_object; +use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::str::DOMString; use crate::dom::globalscope::GlobalScope; -use crate::dom::performance::reduce_timing_resolution; use crate::dom::performanceentry::PerformanceEntry; // TODO UA may choose to limit how many resources are included as PerformanceResourceTiming objects @@ -37,18 +38,30 @@ pub struct PerformanceResourceTiming { entry: PerformanceEntry, initiator_type: InitiatorType, next_hop: Option, - 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, + #[no_trace] + worker_start: Option, + #[no_trace] + redirect_start: Option, + #[no_trace] + redirect_end: Option, + #[no_trace] + fetch_start: Option, + #[no_trace] + domain_lookup_start: Option, + #[no_trace] + domain_lookup_end: Option, + #[no_trace] + connect_start: Option, + #[no_trace] + connect_end: Option, + #[no_trace] + secure_connection_start: Option, + #[no_trace] + request_start: Option, + #[no_trace] + response_start: Option, + #[no_trace] + response_end: Option, transfer_size: u64, //size in octets encoded_body_size: u64, //size in octets decoded_body_size: u64, //size in octets @@ -66,7 +79,7 @@ impl PerformanceResourceTiming { url: ServoUrl, initiator_type: InitiatorType, next_hop: Option, - fetch_start: f64, + fetch_start: Option, ) -> PerformanceResourceTiming { let entry_type = if initiator_type == InitiatorType::Navigation { DOMString::from("navigation") @@ -77,23 +90,23 @@ impl PerformanceResourceTiming { entry: PerformanceEntry::new_inherited( DOMString::from(url.into_string()), entry_type, - 0., - 0., + None, + Duration::ZERO, ), initiator_type, next_hop, - worker_start: 0., - redirect_start: 0., - redirect_end: 0., + worker_start: None, + redirect_start: None, + redirect_end: None, fetch_start, - domain_lookup_end: 0., - domain_lookup_start: 0., - connect_start: 0., - connect_end: 0., - secure_connection_start: 0., - request_start: 0., - response_start: 0., - response_end: 0., + domain_lookup_end: None, + domain_lookup_start: None, + connect_start: None, + connect_end: None, + secure_connection_start: None, + request_start: None, + response_start: None, + response_end: None, transfer_size: 0, encoded_body_size: 0, decoded_body_size: 0, @@ -108,28 +121,32 @@ impl PerformanceResourceTiming { next_hop: Option, resource_timing: &ResourceFetchTiming, ) -> PerformanceResourceTiming { + let duration = match (resource_timing.start_time, resource_timing.response_end) { + (Some(start_time), Some(end_time)) => end_time - start_time, + _ => Duration::ZERO, + }; PerformanceResourceTiming { entry: PerformanceEntry::new_inherited( DOMString::from(url.into_string()), DOMString::from("resource"), - resource_timing.start_time as f64, - resource_timing.response_end as f64 - resource_timing.start_time as f64, + resource_timing.start_time, + duration, ), initiator_type, next_hop, - worker_start: 0., - redirect_start: resource_timing.redirect_start as f64, - redirect_end: resource_timing.redirect_end as f64, - fetch_start: resource_timing.fetch_start as f64, - domain_lookup_start: resource_timing.domain_lookup_start as f64, + worker_start: None, + redirect_start: resource_timing.redirect_start, + redirect_end: resource_timing.redirect_end, + fetch_start: resource_timing.fetch_start, + domain_lookup_start: resource_timing.domain_lookup_start, //TODO (#21260) - domain_lookup_end: 0., - connect_start: resource_timing.connect_start as f64, - connect_end: resource_timing.connect_end as f64, - secure_connection_start: resource_timing.secure_connection_start as f64, - request_start: resource_timing.request_start as f64, - response_start: resource_timing.response_start as f64, - response_end: resource_timing.response_end as f64, + domain_lookup_end: None, + connect_start: resource_timing.connect_start, + connect_end: resource_timing.connect_end, + secure_connection_start: resource_timing.secure_connection_start, + request_start: resource_timing.request_start, + response_start: resource_timing.response_start, + response_end: resource_timing.response_end, transfer_size: 0, encoded_body_size: 0, decoded_body_size: 0, @@ -153,6 +170,18 @@ impl PerformanceResourceTiming { global, ) } + + /// Convert an optional [`CrossProcessInstant`] to a [`DOMHighResTimeStamp`]. If none + /// return a timestamp for [`Self::fetch_start`] instead, so that timestamps are + /// always after that time. + pub(crate) fn to_dom_high_res_time_stamp( + &self, + instant: Option, + ) -> DOMHighResTimeStamp { + self.global() + .performance() + .maybe_to_dom_high_res_time_stamp(instant) + } } // https://w3c.github.io/resource-timing/ @@ -180,17 +209,17 @@ impl PerformanceResourceTimingMethods for PerformanceResourceTiming { // https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-domainlookupstart fn DomainLookupStart(&self) -> DOMHighResTimeStamp { - reduce_timing_resolution(self.domain_lookup_start) + self.to_dom_high_res_time_stamp(self.domain_lookup_start) } // https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-domainlookupend fn DomainLookupEnd(&self) -> DOMHighResTimeStamp { - reduce_timing_resolution(self.domain_lookup_end) + self.to_dom_high_res_time_stamp(self.domain_lookup_end) } // https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-secureconnectionstart fn SecureConnectionStart(&self) -> DOMHighResTimeStamp { - reduce_timing_resolution(self.secure_connection_start) + self.to_dom_high_res_time_stamp(self.secure_connection_start) } // https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-transfersize @@ -210,41 +239,41 @@ impl PerformanceResourceTimingMethods for PerformanceResourceTiming { // https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-requeststart fn RequestStart(&self) -> DOMHighResTimeStamp { - reduce_timing_resolution(self.request_start) + self.to_dom_high_res_time_stamp(self.request_start) } // https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-redirectstart fn RedirectStart(&self) -> DOMHighResTimeStamp { - reduce_timing_resolution(self.redirect_start) + self.to_dom_high_res_time_stamp(self.redirect_start) } // https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-redirectend fn RedirectEnd(&self) -> DOMHighResTimeStamp { - reduce_timing_resolution(self.redirect_end) + self.to_dom_high_res_time_stamp(self.redirect_end) } // https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-responsestart fn ResponseStart(&self) -> DOMHighResTimeStamp { - reduce_timing_resolution(self.response_start) + self.to_dom_high_res_time_stamp(self.response_start) } // https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-fetchstart fn FetchStart(&self) -> DOMHighResTimeStamp { - reduce_timing_resolution(self.fetch_start) + self.to_dom_high_res_time_stamp(self.fetch_start) } // https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-connectstart fn ConnectStart(&self) -> DOMHighResTimeStamp { - reduce_timing_resolution(self.connect_start) + self.to_dom_high_res_time_stamp(self.connect_start) } // https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-connectend fn ConnectEnd(&self) -> DOMHighResTimeStamp { - reduce_timing_resolution(self.connect_end) + self.to_dom_high_res_time_stamp(self.connect_end) } // https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-responseend fn ResponseEnd(&self) -> DOMHighResTimeStamp { - reduce_timing_resolution(self.response_end) + self.to_dom_high_res_time_stamp(self.response_end) } } diff --git a/components/script/dom/performancetiming.rs b/components/script/dom/performancetiming.rs deleted file mode 100644 index b3c2ee426d8..00000000000 --- a/components/script/dom/performancetiming.rs +++ /dev/null @@ -1,103 +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/. */ - -use crate::dom::bindings::codegen::Bindings::PerformanceTimingBinding; -use crate::dom::bindings::codegen::Bindings::PerformanceTimingBinding::PerformanceTimingMethods; -use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; -use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; -use crate::dom::bindings::root::{Dom, DomRoot}; -use crate::dom::document::Document; -use crate::dom::window::Window; -use dom_struct::dom_struct; - -#[dom_struct] -pub struct PerformanceTiming { - reflector_: Reflector, - navigation_start: u64, - navigation_start_precise: u64, - document: Dom, -} - -impl PerformanceTiming { - fn new_inherited( - nav_start: u64, - nav_start_precise: u64, - document: &Document, - ) -> PerformanceTiming { - PerformanceTiming { - reflector_: Reflector::new(), - navigation_start: nav_start, - navigation_start_precise: nav_start_precise, - document: Dom::from_ref(document), - } - } - - #[allow(crown::unrooted_must_root)] - pub fn new( - window: &Window, - navigation_start: u64, - navigation_start_precise: u64, - ) -> DomRoot { - let timing = PerformanceTiming::new_inherited( - navigation_start, - navigation_start_precise, - &window.Document(), - ); - reflect_dom_object(Box::new(timing), window) - } -} - -impl PerformanceTimingMethods for PerformanceTiming { - // https://w3c.github.io/navigation-timing/#widl-PerformanceTiming-navigationStart - fn NavigationStart(&self) -> u64 { - self.navigation_start - } - - // https://w3c.github.io/navigation-timing/#widl-PerformanceTiming-domLoading - fn DomLoading(&self) -> u64 { - self.document.get_dom_loading() - } - - // https://w3c.github.io/navigation-timing/#widl-PerformanceTiming-domInteractive - fn DomInteractive(&self) -> u64 { - self.document.get_dom_interactive() - } - - // https://w3c.github.io/navigation-timing/#widl-PerformanceTiming-domContentLoadedEventStart - fn DomContentLoadedEventStart(&self) -> u64 { - self.document.get_dom_content_loaded_event_start() - } - - // https://w3c.github.io/navigation-timing/#widl-PerformanceTiming-domContentLoadedEventEnd - fn DomContentLoadedEventEnd(&self) -> u64 { - self.document.get_dom_content_loaded_event_end() - } - - // https://w3c.github.io/navigation-timing/#widl-PerformanceTiming-domComplete - fn DomComplete(&self) -> u64 { - self.document.get_dom_complete() - } - - // https://w3c.github.io/navigation-timing/#widl-PerformanceTiming-loadEventStart - fn LoadEventStart(&self) -> u64 { - self.document.get_load_event_start() - } - - // https://w3c.github.io/navigation-timing/#widl-PerformanceTiming-loadEventEnd - fn LoadEventEnd(&self) -> u64 { - self.document.get_load_event_end() - } - - // check-tidy: no specs after this line - // Servo-only timing for when top-level content (not iframes) is complete - fn TopLevelDomComplete(&self) -> u64 { - self.document.get_top_level_dom_complete() - } -} - -impl PerformanceTiming { - pub fn navigation_start_precise(&self) -> u64 { - self.navigation_start_precise - } -} diff --git a/components/script/dom/servoparser/mod.rs b/components/script/dom/servoparser/mod.rs index 9a95210ca6b..f6c7af85838 100644 --- a/components/script/dom/servoparser/mod.rs +++ b/components/script/dom/servoparser/mod.rs @@ -5,6 +5,7 @@ use std::borrow::Cow; use std::cell::Cell; +use base::cross_process_instant::CrossProcessInstant; use base::id::PipelineId; use base64::engine::general_purpose; use base64::Engine as _; @@ -937,11 +938,15 @@ impl FetchResponseListener for ParserContext { parser.parse_sync(); } - //TODO only update if this is the current document resource + // TODO: Only update if this is the current document resource. + // TODO(mrobinson): Pass a proper fetch_start parameter here instead of `CrossProcessInstant::now()`. if let Some(pushed_index) = self.pushed_entry_index { let document = &parser.document; - let performance_entry = - PerformanceNavigationTiming::new(&document.global(), 0, 0, document); + let performance_entry = PerformanceNavigationTiming::new( + &document.global(), + CrossProcessInstant::now(), + document, + ); document .global() .performance() @@ -969,9 +974,12 @@ impl FetchResponseListener for ParserContext { let document = &parser.document; - //TODO nav_start and nav_start_precise - let performance_entry = - PerformanceNavigationTiming::new(&document.global(), 0, 0, document); + // TODO: Pass a proper fetch start time here. + let performance_entry = PerformanceNavigationTiming::new( + &document.global(), + CrossProcessInstant::now(), + document, + ); self.pushed_entry_index = document .global() .performance() diff --git a/components/script/dom/visibilitystateentry.rs b/components/script/dom/visibilitystateentry.rs index 9dcf74a58e6..c08eac70b56 100644 --- a/components/script/dom/visibilitystateentry.rs +++ b/components/script/dom/visibilitystateentry.rs @@ -4,7 +4,9 @@ use std::ops::Deref; +use base::cross_process_instant::CrossProcessInstant; use dom_struct::dom_struct; +use time_03::Duration; use crate::dom::bindings::codegen::Bindings::DocumentBinding::DocumentVisibilityState; use crate::dom::bindings::codegen::Bindings::PerformanceEntryBinding::PerformanceEntryMethods; @@ -23,7 +25,10 @@ pub struct VisibilityStateEntry { impl VisibilityStateEntry { #[allow(crown::unrooted_must_root)] - fn new_inherited(state: DocumentVisibilityState, timestamp: f64) -> VisibilityStateEntry { + fn new_inherited( + state: DocumentVisibilityState, + timestamp: CrossProcessInstant, + ) -> VisibilityStateEntry { let name = match state { DocumentVisibilityState::Visible => DOMString::from("visible"), DocumentVisibilityState::Hidden => DOMString::from("hidden"), @@ -32,8 +37,8 @@ impl VisibilityStateEntry { entry: PerformanceEntry::new_inherited( name, DOMString::from("visibility-state"), - timestamp, - 0., + Some(timestamp), + Duration::ZERO, ), } } @@ -41,7 +46,7 @@ impl VisibilityStateEntry { pub fn new( global: &GlobalScope, state: DocumentVisibilityState, - timestamp: f64, + timestamp: CrossProcessInstant, ) -> DomRoot { reflect_dom_object( Box::new(VisibilityStateEntry::new_inherited(state, timestamp)), diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index a815647e1d1..0540cabeab5 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -17,6 +17,7 @@ use std::{cmp, env}; use app_units::Au; use backtrace::Backtrace; +use base::cross_process_instant::CrossProcessInstant; use base::id::{BrowsingContextId, PipelineId}; use base64::Engine; use bluetooth_traits::BluetoothRequest; @@ -201,8 +202,8 @@ pub struct Window { history: MutNullableDom, custom_element_registry: MutNullableDom, performance: MutNullableDom, - navigation_start: Cell, - navigation_start_precise: Cell, + #[no_trace] + navigation_start: Cell, screen: MutNullableDom, session_storage: MutNullableDom, local_storage: MutNullableDom, @@ -985,7 +986,7 @@ impl WindowMethods for Window { fn Performance(&self) -> DomRoot { self.performance.or_init(|| { let global_scope = self.upcast::(); - Performance::new(global_scope, self.navigation_start_precise.get()) + Performance::new(global_scope, self.navigation_start.get()) }) } @@ -1625,10 +1626,6 @@ impl Window { self.paint_worklet.or_init(|| self.new_paint_worklet()) } - pub fn get_navigation_start(&self) -> u64 { - self.navigation_start_precise.get() - } - pub fn has_document(&self) -> bool { self.document.get().is_some() } @@ -2494,10 +2491,7 @@ impl Window { } pub fn set_navigation_start(&self) { - let current_time = time::get_time(); - let now = (current_time.sec * 1000 + current_time.nsec as i64 / 1000000) as u64; - self.navigation_start.set(now); - self.navigation_start_precise.set(time::precise_time_ns()); + self.navigation_start.set(CrossProcessInstant::now()); } pub fn send_to_embedder(&self, msg: EmbedderMsg) { @@ -2547,8 +2541,7 @@ impl Window { window_size: WindowSizeData, origin: MutableOrigin, creator_url: ServoUrl, - navigation_start: u64, - navigation_start_precise: u64, + navigation_start: CrossProcessInstant, webgl_chan: Option, webxr_registry: webxr_api::Registry, microtask_queue: Rc, @@ -2606,7 +2599,6 @@ impl Window { document: Default::default(), performance: Default::default(), navigation_start: Cell::new(navigation_start), - navigation_start_precise: Cell::new(navigation_start_precise), screen: Default::default(), session_storage: Default::default(), local_storage: Default::default(), diff --git a/components/script/dom/workerglobalscope.rs b/components/script/dom/workerglobalscope.rs index fb1baed40f7..f45ac219284 100644 --- a/components/script/dom/workerglobalscope.rs +++ b/components/script/dom/workerglobalscope.rs @@ -8,6 +8,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use std::time::Duration; +use base::cross_process_instant::CrossProcessInstant; use base::id::{PipelineId, PipelineNamespace}; use crossbeam_channel::Receiver; use devtools_traits::{DevtoolScriptControlMsg, WorkerId}; @@ -22,7 +23,6 @@ use net_traits::request::{ use net_traits::IpcSend; use script_traits::WorkerGlobalScopeInit; use servo_url::{MutableOrigin, ServoUrl}; -use time::precise_time_ns; use uuid::Uuid; use super::bindings::codegen::Bindings::MessagePortBinding::StructuredSerializeOptions; @@ -125,7 +125,8 @@ pub struct WorkerGlobalScope { /// `IpcSender` doesn't exist from_devtools_receiver: Receiver, - navigation_start_precise: u64, + #[no_trace] + navigation_start: CrossProcessInstant, performance: MutNullableDom, } @@ -170,7 +171,7 @@ impl WorkerGlobalScope { navigator: Default::default(), from_devtools_sender: init.from_devtools_sender, from_devtools_receiver, - navigation_start_precise: precise_time_ns(), + navigation_start: CrossProcessInstant::now(), performance: Default::default(), } } @@ -415,7 +416,7 @@ impl WorkerGlobalScopeMethods for WorkerGlobalScope { fn Performance(&self) -> DomRoot { self.performance.or_init(|| { let global_scope = self.upcast::(); - Performance::new(global_scope, self.navigation_start_precise) + Performance::new(global_scope, self.navigation_start) }) } diff --git a/components/script/dom/xrsession.rs b/components/script/dom/xrsession.rs index 928dfa44763..f4d3836f60b 100644 --- a/components/script/dom/xrsession.rs +++ b/components/script/dom/xrsession.rs @@ -8,6 +8,7 @@ use std::f64::consts::{FRAC_PI_2, PI}; use std::rc::Rc; use std::{mem, ptr}; +use base::cross_process_instant::CrossProcessInstant; use dom_struct::dom_struct; use euclid::{RigidTransform3D, Transform3D, Vector3D}; use ipc_channel::ipc::IpcReceiver; @@ -15,7 +16,6 @@ use ipc_channel::router::ROUTER; use js::jsapi::JSObject; use js::jsval::JSVal; use js::typedarray::Float32Array; -use metrics::ToMs; use profile_traits::ipc; use servo_atoms::Atom; use webxr_api::{ @@ -52,7 +52,6 @@ use crate::dom::bindings::utils::to_frozen_array; use crate::dom::event::Event; use crate::dom::eventtarget::EventTarget; use crate::dom::globalscope::GlobalScope; -use crate::dom::performance::reduce_timing_resolution; use crate::dom::promise::Promise; use crate::dom::xrboundedreferencespace::XRBoundedReferenceSpace; use crate::dom::xrframe::XRFrame; @@ -203,7 +202,7 @@ impl XRSession { frame_receiver.to_opaque(), Box::new(move |message| { let frame: Frame = message.to().unwrap(); - let time = time::precise_time_ns(); + let time = CrossProcessInstant::now(); let this = this.clone(); let _ = task_source.queue_with_canceller( task!(xr_raf_callback: move || { @@ -377,7 +376,7 @@ impl XRSession { } /// - fn raf_callback(&self, mut frame: Frame, time: u64) { + fn raf_callback(&self, mut frame: Frame, time: CrossProcessInstant) { debug!("WebXR RAF callback {:?}", frame); // Step 1-2 happen in the xebxr device thread @@ -427,10 +426,10 @@ impl XRSession { assert!(current.is_empty()); mem::swap(&mut *self.raf_callback_list.borrow_mut(), &mut current); } - let start = self.global().as_window().get_navigation_start(); - let time = reduce_timing_resolution((time - start).to_ms()); + let time = self.global().performance().to_dom_high_res_time_stamp(time); let frame = XRFrame::new(&self.global(), self, frame); + // Step 8-9 frame.set_active(true); frame.set_animation_frame(true); diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 276c25ba4e7..a7c91cc9a49 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -27,12 +27,13 @@ use std::result::Result; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use std::thread; -use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; +use std::time::{Duration, Instant, SystemTime}; use background_hang_monitor_api::{ BackgroundHangMonitor, BackgroundHangMonitorExitSignal, HangAnnotation, MonitoredComponentId, MonitoredComponentType, ScriptHangAnnotation, }; +use base::cross_process_instant::CrossProcessInstant; use base::id::{ BrowsingContextId, HistoryStateId, PipelineId, PipelineNamespace, TopLevelBrowsingContextId, }; @@ -91,7 +92,6 @@ use servo_config::opts; use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl}; use style::dom::OpaqueNode; use style::thread_state::{self, ThreadState}; -use time::precise_time_ns; use url::Position; use webgpu::{WebGPUDevice, WebGPUMsg}; use webrender_api::DocumentId; @@ -213,10 +213,9 @@ struct InProgressLoad { /// The origin for the document #[no_trace] origin: MutableOrigin, - /// Timestamp reporting the time in milliseconds when the browser started this load. - navigation_start: u64, - /// High res timestamp reporting the time in nanoseconds when the browser started this load. - navigation_start_precise: u64, + /// Timestamp reporting the time when the browser started this load. + #[no_trace] + navigation_start: CrossProcessInstant, /// For cancelling the fetch canceller: FetchCanceller, /// If inheriting the security context @@ -237,11 +236,7 @@ impl InProgressLoad { origin: MutableOrigin, inherited_secure_context: Option, ) -> InProgressLoad { - let duration = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap_or_default(); - let navigation_start = duration.as_millis(); - let navigation_start_precise = precise_time_ns(); + let navigation_start = CrossProcessInstant::now(); InProgressLoad { pipeline_id: id, browsing_context_id, @@ -253,8 +248,7 @@ impl InProgressLoad { throttled: false, url, origin, - navigation_start: navigation_start as u64, - navigation_start_precise, + navigation_start, canceller: Default::default(), inherited_secure_context, } @@ -2430,7 +2424,12 @@ impl ScriptThread { ) } - fn handle_set_epoch_paint_time(&self, pipeline_id: PipelineId, epoch: Epoch, time: u64) { + fn handle_set_epoch_paint_time( + &self, + pipeline_id: PipelineId, + epoch: Epoch, + time: CrossProcessInstant, + ) { let Some(window) = self.documents.borrow().find_window(pipeline_id) else { warn!("Received set epoch paint time message for closed pipeline {pipeline_id}."); return; @@ -3595,7 +3594,7 @@ impl ScriptThread { self.layout_to_constellation_chan.clone(), self.control_chan.clone(), final_url.clone(), - incomplete.navigation_start_precise, + incomplete.navigation_start, ); let layout_config = LayoutConfig { @@ -3635,7 +3634,6 @@ impl ScriptThread { origin.clone(), final_url.clone(), incomplete.navigation_start, - incomplete.navigation_start_precise, self.webgl_chan.as_ref().map(|chan| chan.channel()), self.webxr_registry.clone(), self.microtask_queue.clone(), @@ -3770,7 +3768,7 @@ impl ScriptThread { ); document.set_https_state(metadata.https_state); - document.set_navigation_start(incomplete.navigation_start_precise); + document.set_navigation_start(incomplete.navigation_start); if is_html_document == IsHTMLDocument::NonHTMLDocument { ServoParser::parse_xml_document(&document, None, final_url); @@ -4161,7 +4159,7 @@ impl ScriptThread { &self, pipeline_id: PipelineId, metric_type: ProgressiveWebMetricType, - metric_value: u64, + metric_value: CrossProcessInstant, ) { let window = self.documents.borrow().find_window(pipeline_id); if let Some(window) = window { diff --git a/components/shared/base/Cargo.toml b/components/shared/base/Cargo.toml index ec01fb5cc20..ea7426e83ef 100644 --- a/components/shared/base/Cargo.toml +++ b/components/shared/base/Cargo.toml @@ -20,4 +20,14 @@ malloc_size_of_derive = { workspace = true } parking_lot = { workspace = true } serde = { workspace = true } size_of_test = { workspace = true } +time_03 = { workspace = true } webrender_api = { workspace = true } + +[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies] +mach2 = { workspace = true } + +[target.'cfg(all(unix, not(any(target_os = "macos", target_os = "ios"))))'.dependencies] +libc = { workspace = true } + +[target.'cfg(target_os = "windows")'.dependencies] +windows-sys = { workspace = true, features = ["Win32_System_Performance"] } diff --git a/components/shared/base/cross_process_instant.rs b/components/shared/base/cross_process_instant.rs new file mode 100644 index 00000000000..aa504b8b389 --- /dev/null +++ b/components/shared/base/cross_process_instant.rs @@ -0,0 +1,167 @@ +// Copyright 2024 The Servo Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! An implementation of a monotonic, nanosecond precision timer, like [`std::time::Instant`] that +//! can be serialized and compared across processes. + +use std::ops::{Add, Sub}; + +use malloc_size_of_derive::MallocSizeOf; +use serde::{Deserialize, Serialize}; +use time_03::Duration; + +/// A monotonic, nanosecond precision timer that can be used cross-process. The value +/// stored internally is purposefully opaque as the origin is platform-specific. They can +/// be compared and [`time_03::Duration`] can be found by subtracting one from another. +/// The `time` crate is used in this case instead of `std::time` so that durations can +/// be negative. +#[derive( + Clone, Copy, Debug, Deserialize, Eq, MallocSizeOf, Ord, PartialEq, PartialOrd, Serialize, +)] +pub struct CrossProcessInstant { + value: u64, +} + +impl CrossProcessInstant { + pub fn now() -> Self { + Self { + value: platform::now(), + } + } + + /// Some unspecified time epoch. This is mainly useful for converting DOM's `timeOrigin` into a + /// `DOMHighResolutionTimestamp`. See . + pub fn epoch() -> Self { + Self { value: 0 } + } +} + +impl Sub for CrossProcessInstant { + type Output = Duration; + + fn sub(self, rhs: Self) -> Self::Output { + Duration::nanoseconds(self.value as i64 - rhs.value as i64) + } +} + +impl Add for CrossProcessInstant { + type Output = Self; + + fn add(self, rhs: Duration) -> Self::Output { + Self { + value: self.value + rhs.whole_nanoseconds() as u64, + } + } +} + +#[cfg(all(unix, not(any(target_os = "macos", target_os = "ios"))))] +mod platform { + use libc::timespec; + + #[allow(unsafe_code)] + pub(super) fn now() -> u64 { + // SAFETY: libc::timespec is zero initializable. + let time = unsafe { + let mut time: timespec = std::mem::zeroed(); + libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut time); + time + }; + (time.tv_sec as u64) * 1000000000 + (time.tv_nsec as u64) + } +} + +#[cfg(any(target_os = "macos", target_os = "ios"))] +mod platform { + use std::sync::LazyLock; + + use mach2::mach_time::{mach_absolute_time, mach_timebase_info}; + + #[allow(unsafe_code)] + fn timebase_info() -> &'static mach_timebase_info { + static TIMEBASE_INFO: LazyLock = LazyLock::new(|| { + let mut timebase_info = mach_timebase_info { numer: 0, denom: 0 }; + unsafe { mach_timebase_info(&mut timebase_info) }; + timebase_info + }); + &*TIMEBASE_INFO + } + + #[allow(unsafe_code)] + pub(super) fn now() -> u64 { + let timebase_info = timebase_info(); + let absolute_time = unsafe { mach_absolute_time() }; + absolute_time * timebase_info.numer as u64 / timebase_info.denom as u64 + } +} + +#[cfg(target_os = "windows")] +mod platform { + use std::sync::atomic::{AtomicU64, Ordering}; + + use windows_sys::Win32::System::Performance::{ + QueryPerformanceCounter, QueryPerformanceFrequency, + }; + + /// The frequency of the value returned by `QueryPerformanceCounter` in counts per + /// second. This is taken from the Rust source code at: + /// + #[allow(unsafe_code)] + fn frequency() -> i64 { + // Either the cached result of `QueryPerformanceFrequency` or `0` for + // uninitialized. Storing this as a single `AtomicU64` allows us to use + // `Relaxed` operations, as we are only interested in the effects on a + // single memory location. + static FREQUENCY: AtomicU64 = AtomicU64::new(0); + + let cached = FREQUENCY.load(Ordering::Relaxed); + // If a previous thread has filled in this global state, use that. + if cached != 0 { + return cached as i64; + } + // ... otherwise learn for ourselves ... + let mut frequency = 0; + let result = unsafe { QueryPerformanceFrequency(&mut frequency) }; + + if result == 0 { + return 0; + } + + FREQUENCY.store(frequency as u64, Ordering::Relaxed); + frequency + } + + #[allow(unsafe_code)] + /// Get the current instant value in nanoseconds. + /// Originally from: + pub(super) fn now() -> u64 { + let mut counter_value = 0; + unsafe { QueryPerformanceCounter(&mut counter_value) }; + + /// Computes (value*numer)/denom without overflow, as long as both + /// (numer*denom) and the overall result fit into i64 (which is the case + /// for our time conversions). + /// Originally from: + fn mul_div_u64(value: u64, numer: u64, denom: u64) -> u64 { + let q = value / denom; + let r = value % denom; + // Decompose value as (value/denom*denom + value%denom), + // substitute into (value*numer)/denom and simplify. + // r < denom, so (denom*numer) is the upper bound of (r*numer) + q * numer + r * numer / denom + } + + static NANOSECONDS_PER_SECOND: u64 = 1_000_000_000; + mul_div_u64( + counter_value as u64, + NANOSECONDS_PER_SECOND, + frequency() as u64, + ) + } +} diff --git a/components/shared/base/lib.rs b/components/shared/base/lib.rs index 062ece48e21..1c5b6472046 100644 --- a/components/shared/base/lib.rs +++ b/components/shared/base/lib.rs @@ -9,6 +9,7 @@ //! You should almost never need to add a data type to this crate. Instead look for //! a more shared crate that has fewer dependents. +pub mod cross_process_instant; pub mod generic_channel; pub mod id; pub mod print_tree; diff --git a/components/shared/devtools/lib.rs b/components/shared/devtools/lib.rs index a7dfc0a0262..82a621966d7 100644 --- a/components/shared/devtools/lib.rs +++ b/components/shared/devtools/lib.rs @@ -341,8 +341,8 @@ pub struct HttpRequest { pub pipeline_id: PipelineId, pub started_date_time: SystemTime, pub time_stamp: i64, - pub connect_time: u64, - pub send_time: u64, + pub connect_time: Duration, + pub send_time: Duration, pub is_xhr: bool, } diff --git a/components/shared/net/lib.rs b/components/shared/net/lib.rs index 554f2334b8a..81ad50b517d 100644 --- a/components/shared/net/lib.rs +++ b/components/shared/net/lib.rs @@ -6,8 +6,8 @@ use std::fmt::Display; use std::sync::LazyLock; -use std::time::{SystemTime, UNIX_EPOCH}; +use base::cross_process_instant::CrossProcessInstant; use base::id::HistoryStateId; use cookie::Cookie; use headers::{ContentType, HeaderMapExt, ReferrerPolicy as ReferrerPolicyHeader}; @@ -20,7 +20,6 @@ use ipc_channel::Error as IpcError; use malloc_size_of::malloc_size_of_is_0; use malloc_size_of_derive::MallocSizeOf; use mime::Mime; -use num_traits::Zero; use rustls::Certificate; use serde::{Deserialize, Serialize}; use servo_rand::RngCore; @@ -494,21 +493,21 @@ pub struct ResourceCorsData { #[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)] pub struct ResourceFetchTiming { - pub domain_lookup_start: u64, + pub domain_lookup_start: Option, pub timing_check_passed: bool, pub timing_type: ResourceTimingType, /// Number of redirects until final resource (currently limited to 20) pub redirect_count: u16, - pub request_start: u64, - pub secure_connection_start: u64, - pub response_start: u64, - pub fetch_start: u64, - pub response_end: u64, - pub redirect_start: u64, - pub redirect_end: u64, - pub connect_start: u64, - pub connect_end: u64, - pub start_time: u64, + pub request_start: Option, + pub secure_connection_start: Option, + pub response_start: Option, + pub fetch_start: Option, + pub response_end: Option, + pub redirect_start: Option, + pub redirect_end: Option, + pub connect_start: Option, + pub connect_end: Option, + pub start_time: Option, } pub enum RedirectStartValue { @@ -539,8 +538,8 @@ pub enum ResourceAttribute { RedirectStart(RedirectStartValue), RedirectEnd(RedirectEndValue), FetchStart, - ConnectStart(u64), - ConnectEnd(u64), + ConnectStart(CrossProcessInstant), + ConnectEnd(CrossProcessInstant), SecureConnectionStart, ResponseEnd, StartTime(ResourceTimeValue), @@ -559,18 +558,18 @@ impl ResourceFetchTiming { ResourceFetchTiming { timing_type, timing_check_passed: true, - domain_lookup_start: 0, + domain_lookup_start: None, redirect_count: 0, - secure_connection_start: 0, - request_start: 0, - response_start: 0, - fetch_start: 0, - redirect_start: 0, - redirect_end: 0, - connect_start: 0, - connect_end: 0, - response_end: 0, - start_time: 0, + secure_connection_start: None, + request_start: None, + response_start: None, + fetch_start: None, + redirect_start: None, + redirect_end: None, + connect_start: None, + connect_end: None, + response_end: None, + start_time: None, } } @@ -586,47 +585,41 @@ impl ResourceFetchTiming { if !self.timing_check_passed && !should_attribute_always_be_updated { return; } - let now = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap_or_default() - .as_nanos() as u64; + let now = Some(CrossProcessInstant::now()); match attribute { ResourceAttribute::DomainLookupStart => self.domain_lookup_start = now, ResourceAttribute::RedirectCount(count) => self.redirect_count = count, ResourceAttribute::RequestStart => self.request_start = now, ResourceAttribute::ResponseStart => self.response_start = now, ResourceAttribute::RedirectStart(val) => match val { - RedirectStartValue::Zero => self.redirect_start = 0, + RedirectStartValue::Zero => self.redirect_start = None, RedirectStartValue::FetchStart => { - if self.redirect_start.is_zero() { + if self.redirect_start.is_none() { self.redirect_start = self.fetch_start } }, }, ResourceAttribute::RedirectEnd(val) => match val { - RedirectEndValue::Zero => self.redirect_end = 0, + RedirectEndValue::Zero => self.redirect_end = None, RedirectEndValue::ResponseEnd => self.redirect_end = self.response_end, }, ResourceAttribute::FetchStart => self.fetch_start = now, - ResourceAttribute::ConnectStart(val) => self.connect_start = val, - ResourceAttribute::ConnectEnd(val) => self.connect_end = val, + ResourceAttribute::ConnectStart(instant) => self.connect_start = Some(instant), + ResourceAttribute::ConnectEnd(instant) => self.connect_end = Some(instant), ResourceAttribute::SecureConnectionStart => self.secure_connection_start = now, ResourceAttribute::ResponseEnd => self.response_end = now, ResourceAttribute::StartTime(val) => match val { ResourceTimeValue::RedirectStart - if self.redirect_start.is_zero() || !self.timing_check_passed => {}, + if self.redirect_start.is_none() || !self.timing_check_passed => {}, _ => self.start_time = self.get_time_value(val), }, } } - fn get_time_value(&self, time: ResourceTimeValue) -> u64 { + fn get_time_value(&self, time: ResourceTimeValue) -> Option { match time { - ResourceTimeValue::Zero => 0, - ResourceTimeValue::Now => SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap_or_default() - .as_nanos() as u64, + ResourceTimeValue::Zero => None, + ResourceTimeValue::Now => Some(CrossProcessInstant::now()), ResourceTimeValue::FetchStart => self.fetch_start, ResourceTimeValue::RedirectStart => self.redirect_start, } @@ -634,13 +627,13 @@ impl ResourceFetchTiming { pub fn mark_timing_check_failed(&mut self) { self.timing_check_passed = false; - self.domain_lookup_start = 0; + self.domain_lookup_start = None; self.redirect_count = 0; - self.request_start = 0; - self.response_start = 0; - self.redirect_start = 0; - self.connect_start = 0; - self.connect_end = 0; + self.request_start = None; + self.response_start = None; + self.redirect_start = None; + self.connect_start = None; + self.connect_end = None; } } diff --git a/components/shared/net/tests/lib.rs b/components/shared/net/tests/lib.rs index 290ca902935..ab657052819 100644 --- a/components/shared/net/tests/lib.rs +++ b/components/shared/net/tests/lib.rs @@ -2,16 +2,20 @@ * 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 base::cross_process_instant::CrossProcessInstant; use net_traits::{ResourceAttribute, ResourceFetchTiming, ResourceTimeValue, ResourceTimingType}; #[test] fn test_set_start_time_to_fetch_start_if_nonzero_tao() { let mut resource_timing: ResourceFetchTiming = ResourceFetchTiming::new(ResourceTimingType::Resource); - resource_timing.fetch_start = 1; - assert_eq!(resource_timing.start_time, 0, "`start_time` should be zero"); + resource_timing.fetch_start = Some(CrossProcessInstant::now()); assert!( - resource_timing.fetch_start > 0, + resource_timing.start_time.is_none(), + "`start_time` should be zero" + ); + assert!( + resource_timing.fetch_start.is_some(), "`fetch_start` should have a positive value" ); @@ -27,13 +31,13 @@ fn test_set_start_time_to_fetch_start_if_nonzero_tao() { fn test_set_start_time_to_fetch_start_if_zero_tao() { let mut resource_timing: ResourceFetchTiming = ResourceFetchTiming::new(ResourceTimingType::Resource); - resource_timing.start_time = 1; + resource_timing.start_time = Some(CrossProcessInstant::now()); assert!( - resource_timing.start_time > 0, + resource_timing.start_time.is_some(), "`start_time` should have a positive value" ); - assert_eq!( - resource_timing.fetch_start, 0, + assert!( + resource_timing.fetch_start.is_none(), "`fetch_start` should be zero" ); @@ -50,10 +54,13 @@ fn test_set_start_time_to_fetch_start_if_nonzero_no_tao() { let mut resource_timing: ResourceFetchTiming = ResourceFetchTiming::new(ResourceTimingType::Resource); resource_timing.mark_timing_check_failed(); - resource_timing.fetch_start = 1; - assert_eq!(resource_timing.start_time, 0, "`start_time` should be zero"); + resource_timing.fetch_start = Some(CrossProcessInstant::now()); assert!( - resource_timing.fetch_start > 0, + resource_timing.start_time.is_none(), + "`start_time` should be zero" + ); + assert!( + !resource_timing.fetch_start.is_none(), "`fetch_start` should have a positive value" ); @@ -70,13 +77,13 @@ fn test_set_start_time_to_fetch_start_if_zero_no_tao() { let mut resource_timing: ResourceFetchTiming = ResourceFetchTiming::new(ResourceTimingType::Resource); resource_timing.mark_timing_check_failed(); - resource_timing.start_time = 1; + resource_timing.start_time = Some(CrossProcessInstant::now()); assert!( - resource_timing.start_time > 0, + resource_timing.start_time.is_some(), "`start_time` should have a positive value" ); - assert_eq!( - resource_timing.fetch_start, 0, + assert!( + resource_timing.fetch_start.is_none(), "`fetch_start` should be zero" ); @@ -92,10 +99,13 @@ fn test_set_start_time_to_fetch_start_if_zero_no_tao() { fn test_set_start_time_to_redirect_start_if_nonzero_tao() { let mut resource_timing: ResourceFetchTiming = ResourceFetchTiming::new(ResourceTimingType::Resource); - resource_timing.redirect_start = 1; - assert_eq!(resource_timing.start_time, 0, "`start_time` should be zero"); + resource_timing.redirect_start = Some(CrossProcessInstant::now()); assert!( - resource_timing.redirect_start > 0, + resource_timing.start_time.is_none(), + "`start_time` should be zero" + ); + assert!( + resource_timing.redirect_start.is_some(), "`redirect_start` should have a positive value" ); @@ -113,13 +123,13 @@ fn test_set_start_time_to_redirect_start_if_nonzero_tao() { fn test_not_set_start_time_to_redirect_start_if_zero_tao() { let mut resource_timing: ResourceFetchTiming = ResourceFetchTiming::new(ResourceTimingType::Resource); - resource_timing.start_time = 1; + resource_timing.start_time = Some(CrossProcessInstant::now()); assert!( - resource_timing.start_time > 0, + resource_timing.start_time.is_some(), "`start_time` should have a positive value" ); - assert_eq!( - resource_timing.redirect_start, 0, + assert!( + resource_timing.redirect_start.is_none(), "`redirect_start` should be zero" ); @@ -139,10 +149,13 @@ fn test_not_set_start_time_to_redirect_start_if_nonzero_no_tao() { ResourceFetchTiming::new(ResourceTimingType::Resource); resource_timing.mark_timing_check_failed(); // Note: properly-behaved redirect_start should never be nonzero once TAO check has failed - resource_timing.redirect_start = 1; - assert_eq!(resource_timing.start_time, 0, "`start_time` should be zero"); + resource_timing.redirect_start = Some(CrossProcessInstant::now()); assert!( - resource_timing.redirect_start > 0, + resource_timing.start_time.is_none(), + "`start_time` should be zero" + ); + assert!( + resource_timing.redirect_start.is_some(), "`redirect_start` should have a positive value" ); @@ -161,13 +174,13 @@ fn test_not_set_start_time_to_redirect_start_if_zero_no_tao() { let mut resource_timing: ResourceFetchTiming = ResourceFetchTiming::new(ResourceTimingType::Resource); resource_timing.mark_timing_check_failed(); - resource_timing.start_time = 1; + resource_timing.start_time = Some(CrossProcessInstant::now()); assert!( - resource_timing.start_time > 0, + resource_timing.start_time.is_some(), "`start_time` should have a positive value" ); - assert_eq!( - resource_timing.redirect_start, 0, + assert!( + resource_timing.redirect_start.is_none(), "`redirect_start` should be zero" ); @@ -185,28 +198,37 @@ fn test_not_set_start_time_to_redirect_start_if_zero_no_tao() { fn test_set_start_time() { let mut resource_timing: ResourceFetchTiming = ResourceFetchTiming::new(ResourceTimingType::Resource); - assert_eq!(resource_timing.start_time, 0, "`start_time` should be zero"); + assert!( + resource_timing.start_time.is_none(), + "`start_time` should be zero" + ); // verify setting `start_time` to current time succeeds resource_timing.set_attribute(ResourceAttribute::StartTime(ResourceTimeValue::Now)); - assert!(resource_timing.start_time > 0, "failed to set `start_time`"); + assert!( + resource_timing.start_time.is_some(), + "failed to set `start_time`" + ); } #[test] fn test_reset_start_time() { let mut resource_timing: ResourceFetchTiming = ResourceFetchTiming::new(ResourceTimingType::Resource); - assert_eq!(resource_timing.start_time, 0, "`start_time` should be zero"); - - resource_timing.start_time = 1; assert!( - resource_timing.start_time > 0, + resource_timing.start_time.is_none(), + "`start_time` should be zero" + ); + + resource_timing.start_time = Some(CrossProcessInstant::now()); + assert!( + resource_timing.start_time.is_some(), "`start_time` should have a positive value" ); // verify resetting `start_time` (to zero) succeeds resource_timing.set_attribute(ResourceAttribute::StartTime(ResourceTimeValue::Zero)); - assert_eq!( - resource_timing.start_time, 0, + assert!( + resource_timing.start_time.is_none(), "failed to reset `start_time`" ); } diff --git a/components/shared/profile/Cargo.toml b/components/shared/profile/Cargo.toml index 718912a820d..045504652d1 100644 --- a/components/shared/profile/Cargo.toml +++ b/components/shared/profile/Cargo.toml @@ -11,10 +11,11 @@ name = "profile_traits" path = "lib.rs" [dependencies] +base = { workspace = true } crossbeam-channel = { workspace = true } ipc-channel = { workspace = true } log = { workspace = true } serde = { workspace = true } servo_config = { path = "../../config" } signpost = { git = "https://github.com/pcwalton/signpost.git" } -time = { workspace = true } +time_03 = { workspace = true } diff --git a/components/shared/profile/time.rs b/components/shared/profile/time.rs index 2ea952a93ea..62ac03f3e0f 100644 --- a/components/shared/profile/time.rs +++ b/components/shared/profile/time.rs @@ -2,12 +2,12 @@ * 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::time::{SystemTime, UNIX_EPOCH}; - +use base::cross_process_instant::CrossProcessInstant; use ipc_channel::ipc::IpcSender; use log::warn; use serde::{Deserialize, Serialize}; use servo_config::opts; +use time_03::Duration; #[derive(Clone, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)] pub struct TimerMetadata { @@ -30,13 +30,16 @@ impl ProfilerChan { #[derive(Clone, Debug, Deserialize, Serialize)] pub enum ProfilerData { NoRecords, - Record(Vec), + Record(Vec), } #[derive(Clone, Debug, Deserialize, Serialize)] pub enum ProfilerMsg { /// Normal message used for reporting time - Time((ProfilerCategory, Option), (u64, u64)), + Time( + (ProfilerCategory, Option), + (CrossProcessInstant, CrossProcessInstant), + ), /// Message used to get time spend entries for a particular ProfilerBuckets (in nanoseconds) Get( (ProfilerCategory, Option), @@ -139,28 +142,15 @@ where if opts::get().debug.signpost { signpost::start(category as u32, &[0, 0, 0, (category as usize) >> 4]); } - let start_time = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap_or_default() - .as_nanos(); - + let start_time = CrossProcessInstant::now(); let val = callback(); + let end_time = CrossProcessInstant::now(); - let end_time = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap_or_default() - .as_nanos(); if opts::get().debug.signpost { signpost::end(category as u32, &[0, 0, 0, (category as usize) >> 4]); } - send_profile_data( - category, - meta, - &profiler_chan, - start_time as u64, - end_time as u64, - ); + send_profile_data(category, meta, &profiler_chan, start_time, end_time); val } @@ -168,8 +158,8 @@ pub fn send_profile_data( category: ProfilerCategory, meta: Option, profiler_chan: &ProfilerChan, - start_time: u64, - end_time: u64, + start_time: CrossProcessInstant, + end_time: CrossProcessInstant, ) { profiler_chan.send(ProfilerMsg::Time((category, meta), (start_time, end_time))); } diff --git a/components/shared/script/lib.rs b/components/shared/script/lib.rs index d008f572048..0262cb8f785 100644 --- a/components/shared/script/lib.rs +++ b/components/shared/script/lib.rs @@ -21,6 +21,7 @@ use std::sync::Arc; use std::time::Duration; use background_hang_monitor_api::BackgroundHangMonitorRegister; +use base::cross_process_instant::CrossProcessInstant; use base::id::{ BlobId, BrowsingContextId, HistoryStateId, MessagePortId, PipelineId, PipelineNamespaceId, TopLevelBrowsingContextId, @@ -373,7 +374,7 @@ pub enum ConstellationControlMsg { /// Reload the given page. Reload(PipelineId), /// Notifies the script thread about a new recorded paint metric. - PaintMetric(PipelineId, ProgressiveWebMetricType, u64), + PaintMetric(PipelineId, ProgressiveWebMetricType, CrossProcessInstant), /// Notifies the media session about a user requested media session action. MediaSessionAction(PipelineId, MediaSessionActionType), /// Notifies script thread that WebGPU server has started @@ -382,7 +383,7 @@ pub enum ConstellationControlMsg { /// pipeline via the Constellation. SetScrollStates(PipelineId, Vec), /// Send the paint time for a specific epoch. - SetEpochPaintTime(PipelineId, Epoch, u64), + SetEpochPaintTime(PipelineId, Epoch, CrossProcessInstant), } impl fmt::Debug for ConstellationControlMsg { diff --git a/components/shared/script_layout/lib.rs b/components/shared/script_layout/lib.rs index 4c43b2e859a..c5e3bf89d60 100644 --- a/components/shared/script_layout/lib.rs +++ b/components/shared/script_layout/lib.rs @@ -17,6 +17,7 @@ use std::sync::Arc; use app_units::Au; use atomic_refcell::AtomicRefCell; +use base::cross_process_instant::CrossProcessInstant; use base::id::{BrowsingContextId, PipelineId}; use base::Epoch; use canvas_traits::canvas::{CanvasId, CanvasMsg}; @@ -229,7 +230,7 @@ pub trait Layout { fn set_scroll_states(&mut self, scroll_states: &[ScrollState]); /// Set the paint time for a specific epoch. - fn set_epoch_paint_time(&mut self, epoch: Epoch, paint_time: u64); + fn set_epoch_paint_time(&mut self, epoch: Epoch, paint_time: CrossProcessInstant); fn query_content_box(&self, node: OpaqueNode) -> Option>; fn query_content_boxes(&self, node: OpaqueNode) -> Vec>; diff --git a/tests/unit/metrics/interactive_time.rs b/tests/unit/metrics/interactive_time.rs index 0bddb944ace..7a6f2c71ea3 100644 --- a/tests/unit/metrics/interactive_time.rs +++ b/tests/unit/metrics/interactive_time.rs @@ -2,8 +2,7 @@ * 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::time::{SystemTime, UNIX_EPOCH}; - +use base::cross_process_instant::CrossProcessInstant; use ipc_channel::ipc; use metrics::{InteractiveFlag, InteractiveMetrics, ProfilerMetadataFactory, ProgressiveWebMetric}; use profile_traits::time::{ProfilerChan, TimerMetadata}; @@ -25,7 +24,7 @@ fn test_interactive() -> InteractiveMetrics { assert_eq!((&interactive).get_navigation_start(), None); assert_eq!(interactive.get_tti(), None); - interactive.set_navigation_start(time::precise_time_ns()); + interactive.set_navigation_start(CrossProcessInstant::now()); interactive } @@ -56,21 +55,24 @@ fn test_set_mta() { let profiler_metadata_factory = DummyProfilerMetadataFactory {}; let interactive = test_interactive(); - let t = time::precise_time_ns(); + let now = CrossProcessInstant::now(); interactive.maybe_set_tti( &profiler_metadata_factory, - InteractiveFlag::TimeToInteractive(t), + InteractiveFlag::TimeToInteractive(now), ); - let mta = interactive.get_main_thread_available(); - assert!(mta.is_some()); - assert_eq!(mta, Some(t)); + let main_thread_available_time = interactive.get_main_thread_available(); + assert!(main_thread_available_time.is_some()); + assert_eq!(main_thread_available_time, Some(now)); //try to overwrite interactive.maybe_set_tti( &profiler_metadata_factory, - InteractiveFlag::TimeToInteractive(time::precise_time_ns()), + InteractiveFlag::TimeToInteractive(CrossProcessInstant::now()), + ); + assert_eq!( + interactive.get_main_thread_available(), + main_thread_available_time ); - assert_eq!(interactive.get_main_thread_available(), mta); assert_eq!(interactive.get_tti(), None); } @@ -79,23 +81,22 @@ fn test_set_tti_dcl() { let profiler_metadata_factory = DummyProfilerMetadataFactory {}; let interactive = test_interactive(); - let t = time::precise_time_ns(); + let now = CrossProcessInstant::now(); interactive.maybe_set_tti( &profiler_metadata_factory, - InteractiveFlag::TimeToInteractive(t), + InteractiveFlag::TimeToInteractive(now), ); - let mta = interactive.get_main_thread_available(); - assert!(mta.is_some()); + let main_thread_available_time = interactive.get_main_thread_available(); + assert!(main_thread_available_time.is_some()); interactive.maybe_set_tti( &profiler_metadata_factory, InteractiveFlag::DOMContentLoaded, ); - let dcl = interactive.get_dom_content_loaded(); - assert!(dcl.is_some()); + let dom_content_loaded_time = interactive.get_dom_content_loaded(); + assert!(dom_content_loaded_time.is_some()); - let interactive_time = dcl.unwrap() - (&interactive).get_navigation_start().unwrap(); - assert_eq!(interactive.get_tti(), Some(interactive_time)); + assert_eq!(interactive.get_tti(), dom_content_loaded_time); } #[test] @@ -110,19 +111,15 @@ fn test_set_tti_mta() { let dcl = interactive.get_dom_content_loaded(); assert!(dcl.is_some()); - let t = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap_or_default() - .as_nanos() as u64; + let time = CrossProcessInstant::now(); interactive.maybe_set_tti( &profiler_metadata_factory, - InteractiveFlag::TimeToInteractive(t), + InteractiveFlag::TimeToInteractive(time), ); let mta = interactive.get_main_thread_available(); assert!(mta.is_some()); - let interactive_time = mta.unwrap() - (&interactive).get_navigation_start().unwrap(); - assert_eq!(interactive.get_tti(), Some(interactive_time)); + assert_eq!(interactive.get_tti(), mta); } // TODO InteractiveWindow tests diff --git a/tests/unit/metrics/paint_time.rs b/tests/unit/metrics/paint_time.rs index fc708d3d5ab..3da7f86dcf0 100644 --- a/tests/unit/metrics/paint_time.rs +++ b/tests/unit/metrics/paint_time.rs @@ -2,6 +2,7 @@ * 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 base::cross_process_instant::CrossProcessInstant; use base::id::TEST_PIPELINE_ID; use base::Epoch; use ipc_channel::ipc; @@ -22,17 +23,18 @@ fn test_paint_metrics_construction() { let profiler_chan = ProfilerChan(sender); let (layout_sender, _) = ipc::channel().unwrap(); let (script_sender, _) = ipc::channel().unwrap(); + let start_time = CrossProcessInstant::now(); let paint_time_metrics = PaintTimeMetrics::new( TEST_PIPELINE_ID, profiler_chan, layout_sender, script_sender, ServoUrl::parse("about:blank").unwrap(), - 0, + start_time, ); assert_eq!( (&paint_time_metrics).get_navigation_start(), - Some(0), + Some(start_time), "navigation start is set properly" ); assert_eq!( @@ -52,13 +54,14 @@ fn test_common(display_list_is_contentful: bool, epoch: Epoch) -> PaintTimeMetri let profiler_chan = ProfilerChan(sender); let (layout_sender, _) = ipc::channel().unwrap(); let (script_sender, _) = ipc::channel().unwrap(); + let start_time = CrossProcessInstant::now(); let mut paint_time_metrics = PaintTimeMetrics::new( TEST_PIPELINE_ID, profiler_chan, layout_sender, script_sender, ServoUrl::parse("about:blank").unwrap(), - 0, + start_time, ); let dummy_profiler_metadata_factory = DummyProfilerMetadataFactory {}; @@ -79,7 +82,7 @@ fn test_common(display_list_is_contentful: bool, epoch: Epoch) -> PaintTimeMetri "first contentful paint is None" ); - let navigation_start = time::precise_time_ns(); + let navigation_start = CrossProcessInstant::now(); paint_time_metrics.set_navigation_start(navigation_start); assert_eq!( (&paint_time_metrics).get_navigation_start().unwrap(), @@ -94,7 +97,7 @@ fn test_common(display_list_is_contentful: bool, epoch: Epoch) -> PaintTimeMetri fn test_first_paint_setter() { let epoch = Epoch(0); let paint_time_metrics = test_common(false, epoch); - let now = time::precise_time_ns(); + let now = CrossProcessInstant::now(); paint_time_metrics.maybe_set_metric(epoch, now); assert!( paint_time_metrics.get_first_paint().is_some(), @@ -111,7 +114,7 @@ fn test_first_paint_setter() { fn test_first_contentful_paint_setter() { let epoch = Epoch(0); let paint_time_metrics = test_common(true, epoch); - let now = time::precise_time_ns(); + let now = CrossProcessInstant::now(); paint_time_metrics.maybe_set_metric(epoch, now); assert!( paint_time_metrics.get_first_contentful_paint().is_some(), diff --git a/tests/unit/profile/Cargo.toml b/tests/unit/profile/Cargo.toml index 8c2c7c38d15..7b9e8e36bd5 100644 --- a/tests/unit/profile/Cargo.toml +++ b/tests/unit/profile/Cargo.toml @@ -16,4 +16,4 @@ ipc-channel = { workspace = true } profile = { path = "../../../components/profile" } profile_traits = { workspace = true } servo_config = { path = "../../../components/config" } - +time_03 = { workspace = true } diff --git a/tests/unit/profile/time.rs b/tests/unit/profile/time.rs index a96c27c3032..11b70d88e9b 100644 --- a/tests/unit/profile/time.rs +++ b/tests/unit/profile/time.rs @@ -3,13 +3,13 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::thread; -use std::time::Duration; use ipc_channel::ipc; use profile::time; use profile_traits::ipc as ProfiledIpc; use profile_traits::time::{ProfilerCategory, ProfilerData, ProfilerMsg}; use servo_config::opts::OutputOptions; +use time_03::Duration; #[test] fn time_profiler_smoke_test() { @@ -24,24 +24,41 @@ fn time_profiler_smoke_test() { #[test] fn time_profiler_stats_test() { let even_data = vec![ - 1.234, 3.24567, 3.54578, 5.0, 5.324, 7.345, 9.2345, 10.2342345, 13.2599, 15.0, + Duration::seconds_f64(1.234), + Duration::seconds_f64(3.246), + Duration::seconds_f64(3.546), + Duration::seconds_f64(5.000), + Duration::seconds_f64(5.324), + Duration::seconds_f64(7.345), + Duration::seconds_f64(9.235), + Duration::seconds_f64(10.234), + Duration::seconds_f64(13.250), + Duration::seconds_f64(15.000), ]; let (even_mean, even_median, even_min, even_max) = time::Profiler::get_statistics(&even_data); - assert_eq!(7.34230845, even_mean); - assert_eq!(7.345, even_median); - assert_eq!(1.234, even_min); - assert_eq!(15.0, even_max); + assert_eq!(7341, even_mean.whole_milliseconds()); + assert_eq!(Duration::seconds_f64(7.345), even_median); + assert_eq!(Duration::seconds_f64(1.234), even_min); + assert_eq!(Duration::seconds_f64(15.000), even_max); let odd_data = vec![ - 1.234, 3.24567, 3.54578, 5.0, 5.324, 7.345, 9.2345, 10.2342345, 13.2599, + Duration::seconds_f64(1.234), + Duration::seconds_f64(3.246), + Duration::seconds_f64(3.546), + Duration::seconds_f64(5.000), + Duration::seconds_f64(5.324), + Duration::seconds_f64(7.345), + Duration::seconds_f64(9.235), + Duration::seconds_f64(10.234), + Duration::seconds_f64(13.250), ]; let (odd_mean, odd_median, odd_min, odd_max) = time::Profiler::get_statistics(&odd_data); - assert_eq!(6.491453833333334, odd_mean); - assert_eq!(5.324, odd_median); - assert_eq!(1.234, odd_min); - assert_eq!(13.2599, odd_max); + assert_eq!(6490, odd_mean.whole_milliseconds()); + assert_eq!(Duration::seconds_f64(5.324), odd_median); + assert_eq!(Duration::seconds_f64(1.234), odd_min); + assert_eq!(Duration::seconds_f64(13.250), odd_max); } #[test] @@ -49,7 +66,7 @@ fn channel_profiler_test() { let chan = time::Profiler::create(&Some(OutputOptions::Stdout(5.0)), None); let (profiled_sender, profiled_receiver) = ProfiledIpc::channel(chan.clone()).unwrap(); thread::spawn(move || { - thread::sleep(Duration::from_secs(2)); + thread::sleep(std::time::Duration::from_secs(2)); profiled_sender.send(43).unwrap(); }); @@ -64,7 +81,7 @@ fn channel_profiler_test() { match receiver.recv().unwrap() { // asserts that the time spent in the sleeping thread is more than 1500 milliseconds - ProfilerData::Record(time_data) => assert!(time_data[0] > 1.5e3), + ProfilerData::Record(time_data) => assert!(time_data[0] > Duration::milliseconds(1500)), ProfilerData::NoRecords => assert!(false), }; } @@ -74,7 +91,7 @@ fn bytes_channel_profiler_test() { let chan = time::Profiler::create(&Some(OutputOptions::Stdout(5.0)), None); let (profiled_sender, profiled_receiver) = ProfiledIpc::bytes_channel(chan.clone()).unwrap(); thread::spawn(move || { - thread::sleep(Duration::from_secs(2)); + thread::sleep(std::time::Duration::from_secs(2)); profiled_sender.send(&[1, 2, 3]).unwrap(); }); @@ -89,7 +106,7 @@ fn bytes_channel_profiler_test() { match receiver.recv().unwrap() { // asserts that the time spent in the sleeping thread is more than 1500 milliseconds - ProfilerData::Record(time_data) => assert!(time_data[0] > 1.5e3), + ProfilerData::Record(time_data) => assert!(time_data[0] > Duration::milliseconds(1500)), ProfilerData::NoRecords => assert!(false), }; } @@ -98,7 +115,12 @@ fn bytes_channel_profiler_test() { #[test] #[should_panic] fn time_profiler_unsorted_stats_test() { - let unsorted_data = vec![5.0, 7.5, 1.0, 8.9]; + let unsorted_data = vec![ + Duration::microseconds(5000), + Duration::microseconds(7500), + Duration::microseconds(1000), + Duration::microseconds(890), + ]; time::Profiler::get_statistics(&unsorted_data); } diff --git a/tests/wpt/meta/hr-time/timeOrigin.html.ini b/tests/wpt/meta/hr-time/timeOrigin.html.ini index de004be748d..9329aff189e 100644 --- a/tests/wpt/meta/hr-time/timeOrigin.html.ini +++ b/tests/wpt/meta/hr-time/timeOrigin.html.ini @@ -1,6 +1,3 @@ [timeOrigin.html] [Window timeOrigin is close to Date.now() when there is no system clock adjustment.] expected: FAIL - - [Window and worker timeOrigins are close when created one after another.] - expected: FAIL diff --git a/tests/wpt/meta/navigation-timing/secure-connection-start-reuse.https.html.ini b/tests/wpt/meta/navigation-timing/secure-connection-start-reuse.https.html.ini new file mode 100644 index 00000000000..23c04024b9a --- /dev/null +++ b/tests/wpt/meta/navigation-timing/secure-connection-start-reuse.https.html.ini @@ -0,0 +1,3 @@ +[secure-connection-start-reuse.https.html] + [Test that secureConnectionStart value is as expected when reused] + expected: FAIL diff --git a/tests/wpt/meta/navigation-timing/test-timing-attributes-order.html.ini b/tests/wpt/meta/navigation-timing/test-timing-attributes-order.html.ini index 9ff24feef5b..2a47456ae68 100644 --- a/tests/wpt/meta/navigation-timing/test-timing-attributes-order.html.ini +++ b/tests/wpt/meta/navigation-timing/test-timing-attributes-order.html.ini @@ -2,9 +2,6 @@ [window.performance.timing.navigationStart > 0] expected: FAIL - [window.performance.timing.fetchStart > 0] - expected: FAIL - [window.performance.timing.fetchStart >= window.performance.timing.navigationStart] expected: FAIL @@ -52,3 +49,9 @@ [window.performance.timing.unloadEventEnd > 0] expected: FAIL + + [window.performance.timing.domainLookupStart >= window.performance.timing.fetchStart] + expected: FAIL + + [window.performance.timing.domainLookupStart difference with window.performance.timing.fetchStart is 0 or at least 5 microseconds] + expected: FAIL diff --git a/tests/wpt/meta/navigation-timing/test-timing-server-redirect.html.ini b/tests/wpt/meta/navigation-timing/test-timing-server-redirect.html.ini index 10914784f9e..80ef5237c7d 100644 --- a/tests/wpt/meta/navigation-timing/test-timing-server-redirect.html.ini +++ b/tests/wpt/meta/navigation-timing/test-timing-server-redirect.html.ini @@ -17,8 +17,11 @@ [window.performance.timing.redirectEnd > 0] expected: FAIL - [window.performance.timing.fetchStart > 0] - expected: FAIL - [window.performance.timing.requestStart > 0] expected: FAIL + + [window.performance.timing.requestStart >= window.performance.timing.fetchStart] + expected: FAIL + + [window.performance.timing.requestStart difference with window.performance.timing.fetchStart is 0 or at least 5 microseconds] + expected: FAIL diff --git a/tests/wpt/meta/resource-timing/resource_connection_reuse_mixed_content_redirect.html.ini b/tests/wpt/meta/resource-timing/resource_connection_reuse_mixed_content_redirect.html.ini index d36ac2a9704..b28d8f37bb6 100644 --- a/tests/wpt/meta/resource-timing/resource_connection_reuse_mixed_content_redirect.html.ini +++ b/tests/wpt/meta/resource-timing/resource_connection_reuse_mixed_content_redirect.html.ini @@ -1,4 +1,7 @@ [resource_connection_reuse_mixed_content_redirect.html] + [domainLookupEnd and fetchStart should be the same] + expected: FAIL + [connectStart and fetchStart should be the same] expected: FAIL @@ -10,6 +13,3 @@ [domainLookupStart and fetchStart should be the same] expected: FAIL - - [domainLookupEnd and fetchStart should be the same] - expected: FAIL diff --git a/tests/wpt/meta/resource-timing/test_resource_timing.https.html.ini b/tests/wpt/meta/resource-timing/test_resource_timing.https.html.ini index 5a2e10ff69c..dac51c99110 100644 --- a/tests/wpt/meta/resource-timing/test_resource_timing.https.html.ini +++ b/tests/wpt/meta/resource-timing/test_resource_timing.https.html.ini @@ -68,3 +68,6 @@ [PerformanceEntry has correct protocol attribute (xmlhttprequest)] expected: FAIL + + [PerformanceEntry has correct name, initiatorType, startTime, and duration (img)] + expected: FAIL