diff --git a/components/net/fetch/methods.rs b/components/net/fetch/methods.rs index b60265ce2b8..d2250aa897a 100644 --- a/components/net/fetch/methods.rs +++ b/components/net/fetch/methods.rs @@ -21,8 +21,8 @@ use net_traits::filemanager_thread::RelativePos; use net_traits::request::{CredentialsMode, Destination, Referrer, Request, RequestMode}; use net_traits::request::{Origin, ResponseTainting, Window}; use net_traits::response::{Response, ResponseBody, ResponseType}; -use net_traits::ResourceAttribute; use net_traits::{FetchTaskTarget, NetworkError, ReferrerPolicy, ResourceFetchTiming}; +use net_traits::{ResourceAttribute, ResourceTimeValue}; use servo_arc::Arc as ServoArc; use servo_url::ServoUrl; use std::borrow::Cow; @@ -89,12 +89,19 @@ pub type DoneChannel = Option<(Sender, Receiver)>; /// [Fetch](https://fetch.spec.whatwg.org#concept-fetch) pub fn fetch(request: &mut Request, target: Target, context: &FetchContext) { - // Step 7 of https://w3c.github.io/resource-timing/#processing-model + // Steps 7,4 of https://w3c.github.io/resource-timing/#processing-model + // rev order okay since spec says they're equal - https://w3c.github.io/resource-timing/#dfn-starttime context .timing .lock() .unwrap() .set_attribute(ResourceAttribute::FetchStart); + context + .timing + .lock() + .unwrap() + .set_attribute(ResourceAttribute::StartTime(ResourceTimeValue::FetchStart)); + fetch_with_cors_cache(request, &mut CorsCache::new(), target, context); } diff --git a/components/net/http_loader.rs b/components/net/http_loader.rs index baf55b24faa..3bd8acce355 100644 --- a/components/net/http_loader.rs +++ b/components/net/http_loader.rs @@ -43,7 +43,9 @@ use net_traits::request::{RedirectMode, Referrer, Request, RequestBuilder, Reque use net_traits::request::{ResponseTainting, ServiceWorkersMode}; use net_traits::response::{HttpsState, Response, ResponseBody, ResponseType}; use net_traits::{CookieSource, FetchMetadata, NetworkError, ReferrerPolicy}; -use net_traits::{RedirectEndValue, RedirectStartValue, ResourceAttribute, ResourceFetchTiming}; +use net_traits::{ + RedirectEndValue, RedirectStartValue, ResourceAttribute, ResourceFetchTiming, ResourceTimeValue, +}; use openssl::ssl::SslConnectorBuilder; use servo_arc::Arc; use servo_url::{ImmutableOrigin, ServoUrl}; @@ -572,8 +574,6 @@ pub fn http_fetch( // Generally, we use a persistent connection, so we will also set other PerformanceResourceTiming // attributes to this as well (domain_lookup_start, domain_lookup_end, connect_start, connect_end, // secure_connection_start) - // TODO(#21254) also set startTime equal to either fetch_start or redirect_start - // (https://w3c.github.io/resource-timing/#dfn-starttime) context .timing .lock() @@ -737,6 +737,21 @@ pub fn http_redirect_fetch( .unwrap() .set_attribute(ResourceAttribute::FetchStart); + // start_time should equal redirect_start if nonzero; else fetch_start + context + .timing + .lock() + .unwrap() + .set_attribute(ResourceAttribute::StartTime(ResourceTimeValue::FetchStart)); + + context + .timing + .lock() + .unwrap() + .set_attribute(ResourceAttribute::StartTime( + ResourceTimeValue::RedirectStart, + )); // updates start_time only if redirect_start is nonzero (implying TAO) + // Step 5 if request.redirect_count >= 20 { return Response::network_error(NetworkError::Internal("Too many redirects".into())); diff --git a/components/net_traits/lib.rs b/components/net_traits/lib.rs index ae6d8797adb..cdba4f2066d 100644 --- a/components/net_traits/lib.rs +++ b/components/net_traits/lib.rs @@ -474,6 +474,7 @@ pub struct ResourceFetchTiming { pub redirect_end: u64, pub connect_start: u64, pub connect_end: u64, + pub start_time: u64, } pub enum RedirectStartValue { @@ -487,6 +488,15 @@ pub enum RedirectEndValue { ResponseEnd, } +// TODO: refactor existing code to use this enum for setting time attributes +// suggest using this with all time attributes in the future +pub enum ResourceTimeValue { + Zero, + Now, + FetchStart, + RedirectStart, +} + pub enum ResourceAttribute { RedirectCount(u16), DomainLookupStart, @@ -499,6 +509,7 @@ pub enum ResourceAttribute { ConnectEnd(u64), SecureConnectionStart, ResponseEnd, + StartTime(ResourceTimeValue), } #[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)] @@ -525,6 +536,7 @@ impl ResourceFetchTiming { connect_start: 0, connect_end: 0, response_end: 0, + start_time: 0, } } @@ -532,7 +544,9 @@ impl ResourceFetchTiming { // time origin (as described in Performance::now) pub fn set_attribute(&mut self, attribute: ResourceAttribute) { let should_attribute_always_be_updated = match attribute { - ResourceAttribute::FetchStart | ResourceAttribute::ResponseEnd => true, + ResourceAttribute::FetchStart | + ResourceAttribute::ResponseEnd | + ResourceAttribute::StartTime(_) => true, _ => false, }; if !self.timing_check_passed && !should_attribute_always_be_updated { @@ -562,6 +576,20 @@ impl ResourceFetchTiming { self.secure_connection_start = precise_time_ns() }, ResourceAttribute::ResponseEnd => self.response_end = precise_time_ns(), + ResourceAttribute::StartTime(val) => match val { + ResourceTimeValue::RedirectStart + if self.redirect_start == 0 || !self.timing_check_passed => {}, + _ => self.start_time = self.get_time_value(val), + }, + } + } + + fn get_time_value(&self, time: ResourceTimeValue) -> u64 { + match time { + ResourceTimeValue::Zero => 0, + ResourceTimeValue::Now => precise_time_ns(), + ResourceTimeValue::FetchStart => self.fetch_start, + ResourceTimeValue::RedirectStart => self.redirect_start, } } diff --git a/components/net_traits/tests/lib.rs b/components/net_traits/tests/lib.rs new file mode 100644 index 00000000000..d54b77edb6a --- /dev/null +++ b/components/net_traits/tests/lib.rs @@ -0,0 +1,215 @@ +/* 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 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"); + assert!( + resource_timing.fetch_start > 0, + "`fetch_start` should have a positive value" + ); + + // verify that setting `start_time` to `fetch_start` succeeds + resource_timing.set_attribute(ResourceAttribute::StartTime(ResourceTimeValue::FetchStart)); + assert_eq!( + resource_timing.start_time, resource_timing.fetch_start, + "`start_time` should equal `fetch_start`" + ); +} + +#[test] +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; + assert!( + resource_timing.start_time > 0, + "`start_time` should have a positive value" + ); + assert_eq!( + resource_timing.fetch_start, 0, + "`fetch_start` should be zero" + ); + + // verify that setting `start_time` to `fetch_start` succeeds even when `fetch_start` == zero + resource_timing.set_attribute(ResourceAttribute::StartTime(ResourceTimeValue::FetchStart)); + assert_eq!( + resource_timing.start_time, resource_timing.fetch_start, + "`start_time` should equal `fetch_start`" + ); +} + +#[test] +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"); + assert!( + resource_timing.fetch_start > 0, + "`fetch_start` should have a positive value" + ); + + // verify that setting `start_time` to `fetch_start` succeeds even when TAO check failed + resource_timing.set_attribute(ResourceAttribute::StartTime(ResourceTimeValue::FetchStart)); + assert_eq!( + resource_timing.start_time, resource_timing.fetch_start, + "`start_time` should equal `fetch_start`" + ); +} + +#[test] +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; + assert!( + resource_timing.start_time > 0, + "`start_time` should have a positive value" + ); + assert_eq!( + resource_timing.fetch_start, 0, + "`fetch_start` should be zero" + ); + + // verify that setting `start_time` to `fetch_start` succeeds even when `fetch_start`==0 and no TAO + resource_timing.set_attribute(ResourceAttribute::StartTime(ResourceTimeValue::FetchStart)); + assert_eq!( + resource_timing.start_time, resource_timing.fetch_start, + "`start_time` should equal `fetch_start`" + ); +} + +#[test] +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"); + assert!( + resource_timing.redirect_start > 0, + "`redirect_start` should have a positive value" + ); + + // verify that setting `start_time` to `redirect_start` succeeds for nonzero `redirect_start`, TAO pass + resource_timing.set_attribute(ResourceAttribute::StartTime( + ResourceTimeValue::RedirectStart, + )); + assert_eq!( + resource_timing.start_time, resource_timing.redirect_start, + "`start_time` should equal `redirect_start`" + ); +} + +#[test] +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; + assert!( + resource_timing.start_time > 0, + "`start_time` should have a positive value" + ); + assert_eq!( + resource_timing.redirect_start, 0, + "`redirect_start` should be zero" + ); + + // verify that setting `start_time` to `redirect_start` fails if `redirect_start` == 0 + resource_timing.set_attribute(ResourceAttribute::StartTime( + ResourceTimeValue::RedirectStart, + )); + assert_ne!( + resource_timing.start_time, resource_timing.redirect_start, + "`start_time` should *not* equal `redirect_start`" + ); +} + +#[test] +fn test_not_set_start_time_to_redirect_start_if_nonzero_no_tao() { + let mut resource_timing: ResourceFetchTiming = + 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"); + assert!( + resource_timing.redirect_start > 0, + "`redirect_start` should have a positive value" + ); + + // verify that setting `start_time` to `redirect_start` fails if TAO check fails + resource_timing.set_attribute(ResourceAttribute::StartTime( + ResourceTimeValue::RedirectStart, + )); + assert_ne!( + resource_timing.start_time, resource_timing.redirect_start, + "`start_time` should *not* equal `redirect_start`" + ); +} + +#[test] +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; + assert!( + resource_timing.start_time > 0, + "`start_time` should have a positive value" + ); + assert_eq!( + resource_timing.redirect_start, 0, + "`redirect_start` should be zero" + ); + + // verify that setting `start_time` to `redirect_start` fails if `redirect_start`==0 and no TAO + resource_timing.set_attribute(ResourceAttribute::StartTime( + ResourceTimeValue::RedirectStart, + )); + assert_ne!( + resource_timing.start_time, resource_timing.redirect_start, + "`start_time` should *not* equal `redirect_start`" + ); +} + +#[test] +fn test_set_start_time() { + let mut resource_timing: ResourceFetchTiming = + ResourceFetchTiming::new(ResourceTimingType::Resource); + assert_eq!( + resource_timing.start_time, 0, + "initial `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`"); +} +#[test] +fn test_reset_start_time() { + let mut resource_timing: ResourceFetchTiming = + ResourceFetchTiming::new(ResourceTimingType::Resource); + assert_eq!(resource_timing.start_time, 0, "initial `start_time` = 0"); + + resource_timing.start_time = 1; + assert!( + resource_timing.start_time > 0, + "`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, + "failed to reset `start_time`" + ); +} diff --git a/components/script/dom/performanceresourcetiming.rs b/components/script/dom/performanceresourcetiming.rs index d3fc054d27b..735d13a31f4 100644 --- a/components/script/dom/performanceresourcetiming.rs +++ b/components/script/dom/performanceresourcetiming.rs @@ -55,7 +55,6 @@ pub struct PerformanceResourceTiming { decoded_body_size: u64, //size in octets } -// TODO(#21254): startTime // TODO(#21255): duration // TODO(#21269): next_hop // TODO(#21264): worker_start @@ -115,7 +114,7 @@ impl PerformanceResourceTiming { entry: PerformanceEntry::new_inherited( DOMString::from(url.into_string()), DOMString::from("resource"), - 0., + resource_timing.start_time as f64, 0., ), initiator_type: initiator_type, diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index 6274543554e..518c0fdee03 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -19634,7 +19634,7 @@ "testharness" ], "mozilla/window_performance.html": [ - "690870b7080e179481ca0255f7c30337e8b6636a", + "302073e8041763102d678326509d7ef0a1fb5c79", "testharness" ], "mozilla/window_performance_topLevelDomComplete.html": [ diff --git a/tests/wpt/mozilla/meta/mozilla/window_performance.html.ini b/tests/wpt/mozilla/meta/mozilla/window_performance.html.ini deleted file mode 100644 index 9719867b4dc..00000000000 --- a/tests/wpt/mozilla/meta/mozilla/window_performance.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[window_performance.html] - [window_performance] - expected: FAIL - diff --git a/tests/wpt/mozilla/tests/mozilla/window_performance.html b/tests/wpt/mozilla/tests/mozilla/window_performance.html index 690870b7080..302073e8041 100644 --- a/tests/wpt/mozilla/tests/mozilla/window_performance.html +++ b/tests/wpt/mozilla/tests/mozilla/window_performance.html @@ -16,8 +16,13 @@ test(function() { assert_not_equals(entries.length, 0); assert_true(entries[0] instanceof PerformanceNavigationTiming); - // TODO(#21254) navigationTiming/startTime is not fully implemented yet, so this assertion will fail - assert_greater_than(entries[0].startTime, 0); + /* PerformanceResourceTiming */ + var resourceEntries = window.performance.getEntriesByType("resource"); + assert_not_equals(resourceEntries.length, 0); + assert_true(resourceEntries[0] instanceof PerformanceResourceTiming); + // no redirects => 0 < startTime == fetchStart + assert_greater_than(resourceEntries[0].startTime, 0); + assert_equals(resourceEntries[0].startTime, resourceEntries[0].fetchStart); var last = window.performance.now(); assert_greater_than(last, 0);